Compare commits

..

281 Commits

Author SHA1 Message Date
Cory Bennett a54a6eb2d0 refactor, wip 2016-10-01 00:48:42 -07:00
Cory Bennett 070c38520b fix name for branch 2016-10-01 00:43:12 -07:00
Cory Bennett 89175df121 reorg repo, move library code to lib subdir, add glade 2016-09-18 09:25:26 -07:00
Cory Bennett ac0d2a07ce remove extraneous tee 2016-08-29 01:00:35 -07:00
Cory Bennett 413d46d66a reduce mega verbosity 2016-08-29 01:00:01 -07:00
Cory Bennett 68bb0f2014 up plan count for the sleep 2016-08-29 00:58:23 -07:00
Cory Bennett ef49884512 add missing src directory 2016-08-29 00:49:10 -07:00
Cory Bennett 568d453aa8 move start.log to directory 2016-08-29 00:23:05 -07:00
Cory Bennett a2a03c5221 try travis cache and log atlas-run start output 2016-08-29 00:19:27 -07:00
Cory Bennett a69b5ac8e5 break setup to debug in travis 2016-08-29 00:04:21 -07:00
Cory Bennett 10326d146f wait for docker service to start? 2016-08-28 23:47:34 -07:00
Cory Bennett ec4889dd9b make maven cache directory local so we can ensure it is always there for travis testing 2016-08-28 23:36:37 -07:00
Cory Bennett b71e391297 make prove verbose 2016-08-28 23:32:03 -07:00
Cory Bennett 44cdebfcff golint 2016-08-28 23:30:25 -07:00
Cory Bennett a20f233f4b require docker, run prove after build 2016-08-28 23:24:12 -07:00
Cory Bennett cfa25f4c17 use altas-run to build jira test service in docker container 2016-08-28 23:10:54 -07:00
Cory Bennett e4cc9c6967 add "rank" command allow ordering backlog issues in agile projects 2016-08-26 19:44:38 -07:00
Cory Bennett 6d23899dd5 Updated Changelog 2016-08-24 12:27:42 -07:00
coryb f049998b1c Merge pull request #52 from dbrower/master
Prefer transition names which match exactly
2016-08-24 12:26:56 -07:00
Don Brower e40f9c10c3 Prefer transition names which match exactly
Some transitions are substrings of other transitions, and the current
loop sometimes never chose the correct one. For example, it would never
choose "QA" if there were also a transtion "Deploy to QA". Ditto for
"Open" and "Reopen".
2016-08-24 14:36:23 -04:00
Cory Bennett 693b3e4d40 update tempates to make them more readable with space trimming added to go-1.6 2016-08-21 23:51:45 -07:00
Cory Bennett 15db036caf add simple test for the "table" list output 2016-08-21 23:51:30 -07:00
Cory Bennett 61fc22c30c Updated Changelog 2016-08-21 14:39:54 -07:00
Cory Bennett cc3fbee507 make "worklogs" command print output through template
allow "add worklog" command to open edit template
2016-08-21 14:31:30 -07:00
Cory Bennett d08ef155c8 remove extra newline at end of worklogs template 2016-08-21 14:31:11 -07:00
Cory Bennett ab1cd27c1d adding worklog related templates 2016-08-21 14:27:50 -07:00
Cory Bennett 6f38b76fd6 add vet and link make targets 2016-08-21 14:22:27 -07:00
Cory Bennett 154f75ab1e Updated Changelog 2016-08-21 12:42:47 -07:00
Cory Bennett 8bd22a02f5 add GoDoc badge 2016-08-21 10:24:07 -07:00
Cory Bennett b6f0fb1d87 add travis badge 2016-08-21 10:16:06 -07:00
Cory Bennett 5ee9355407 add travis build 2016-08-21 00:46:03 -07:00
Cory Bennett 5a4e17cb49 update for golint 2016-08-21 00:45:07 -07:00
Cory Bennett 355fb4243b fix for go vet 2016-08-19 10:19:04 -07:00
Cory Bennett 543e3c832c [#51] add missing data directory with generated structs 2016-08-19 08:52:42 -07:00
Cory Bennett 3a8687cca5 Updated Changelog 2016-08-12 13:32:13 -07:00
Cory Bennett 7ee40f7dfb ignore t/.jira.d/templates which are exported in 000setup.t 2016-08-12 13:31:16 -07:00
Cory Bennett 7c3d16b619 add tests for "Task Management" type projects 2016-08-12 13:30:24 -07:00
Cory Bennett d4f2f175ff add tests for "Process Management" type projects 2016-08-12 13:29:56 -07:00
Cory Bennett 577f67fbca add tests for "Project Management" type projects 2016-08-12 13:29:01 -07:00
Cory Bennett 2c91905fd7 when running "dups" on a Process Management Project type, you have to start/stop the task to resolve it 2016-08-12 13:27:51 -07:00
Cory Bennett a328c2dec3 allow for defaultResolution option for transition command 2016-08-12 12:13:37 -07:00
Cory Bennett 5303f44078 add tests for Kanban Software Project type issues 2016-08-10 10:52:45 -07:00
Cory Bennett e21d4c4fae add tests for Scrum Software Project type issues 2016-08-10 10:52:17 -07:00
Cory Bennett 96d81268a6 add test for Basic Software Project type issues 2016-08-10 10:51:55 -07:00
Cory Bennett 5ed1d15e2d add comments 2016-08-10 10:50:04 -07:00
Cory Bennett 30d8ace20d add extra "mojira" user for testing voting and ownership reassignment 2016-08-10 10:49:11 -07:00
Cory Bennett 5d39b23b02 add "backlog" command for Kanban related Issues 2016-08-10 10:48:02 -07:00
Cory Bennett 37c07fa0b5 fix --noedit flag with "dups" command 2016-08-09 23:51:04 -07:00
Cory Bennett 6f73b8cf0d add "votes" and "labels" to default view template 2016-08-09 23:48:28 -07:00
Cory Bennett 45dbb00683 gofmt 2016-08-09 23:16:41 -07:00
Cory Bennett d96bd464b1 gofmt 2016-08-09 23:16:20 -07:00
Cory Bennett 30fd301a61 add "blockerType" config param, for issueLinkType use for "blocks" command 2016-08-09 23:09:31 -07:00
Cory Bennett a823c59af1 add "debug" make target 2016-08-09 23:06:46 -07:00
Cory Bennett 4b822b100a update gitter room 2016-08-08 00:10:04 -07:00
Cory Bennett 704bce3b23 fix typo 2016-08-05 13:11:31 -07:00
Cory Bennett 3711ff7202 add gitter badge 2016-08-05 13:09:17 -07:00
Cory Bennett a713cfa360 make the bootstrapping simpler, move more setup to 000setup.t 2016-08-04 11:11:03 -07:00
Cory Bennett 25debaad5b fix links 2016-08-03 09:58:00 -07:00
Cory Bennett b6cc91b934 mention osht for testing 2016-08-03 09:56:54 -07:00
Cory Bennett 403a362c5c start adding some basic integration tests 2016-08-03 00:13:53 -07:00
Cory Bennett 0c807b45fe default issuetype to "Bug" for project that have Bug, otherwise try "Task" 2016-08-03 00:09:09 -07:00
Cory Bennett 8238fe87b6 make view template only show fields that have values 2016-08-02 23:16:08 -07:00
Cory Bennett adc2aceb89 make default create template only display fields if they are valid fields for the project 2016-08-02 22:44:24 -07:00
Cory Bennett f5f3e28b23 ignore empty json fields when processing templates 2016-08-02 22:36:51 -07:00
Cory Bennett 469def0296 allow JIRA_LOG_FORMAT env variable to control log output format 2016-08-02 20:12:30 -07:00
Cory Bennett 752a94d94b remove extraneous debug 2016-08-02 20:06:40 -07:00
Cory Bennett 8ad91be1c6 add logout command
modify password prompt to echo masked password
2016-08-02 20:04:11 -07:00
Cory Bennett f93fe7909c tweak cookies to store hostname
dump all http request/response with --verbose
2016-08-02 19:23:24 -07:00
Cory Bennett f54267b523 load configs in order of closest to cwd (/etc/go-jira.yml is last) 2016-08-02 19:20:23 -07:00
Cory Bennett 54df77d181 [#48] fix quoting for "make install" so it actually installs to homedir 2016-08-01 11:31:06 -07:00
Cory Bennett 642db779db Updated Changelog 2016-07-30 20:13:00 -07:00
Cory Bennett dd7d1ccd3a [#43] add support for jira done|todo|prog commands 2016-07-30 20:05:13 -07:00
Cory Bennett fdce34dfc0 [issue #46] add documentation for how to create/edit templates 2016-07-08 12:12:02 -07:00
coryb e7a78a1cc1 Merge pull request #24 from mikepea/edit_template_common
Reporter is not generally editable.
2016-06-30 08:53:34 -07:00
Cory Bennett 055b61d400 Updated Changelog 2016-06-29 23:11:04 -07:00
Cory Bennett 0980f8e197 [#44] Close tmpfile before rename to work around "The process cannot access the file because it is being used by another process" error on windows. 2016-06-29 23:09:27 -07:00
Cory Bennett 4f0dbf5c7b trim out unused platforms, we can add then back in on request
publish windows binaries as .exe
2016-06-29 23:08:54 -07:00
Cory Bennett 0e4e85aafd fix for versions with 'v' prefix 2016-06-28 23:07:06 -07:00
Cory Bennett fa4ac4b7ec fix v in version prefix 2016-06-28 23:02:03 -07:00
Cory Bennett a86642f2f4 Updated Changelog 2016-06-28 23:00:49 -07:00
Cory Bennett adcedc4855 use USERPROFILE instead of HOME for windows, rework paths to use
filepath.Join for better cross platform support
2016-06-29 06:57:59 +01:00
Cory Bennett d5156d54fe Merge branch 'master' of github.com:Netflix-Skunkworks/go-jira 2016-06-29 01:40:21 +01:00
Cory Bennett ab5c185bf3 make binary name .exe for windows 2016-06-29 01:26:28 +01:00
Cory Bennett 1cf943df13 update Makefile so it builds under cygwin on windows 2016-06-27 15:51:21 -07:00
Cory Bennett 1fef349ba2 fix build under Cygwin on Windows 2016-06-27 15:46:14 -07:00
coryb 42e787e042 Merge pull request #39 from mikepea/system_template_dir
Include templates from a system path
2016-03-30 14:47:17 -07:00
Mike Pountney cf10f53789 Include templates from a system path
I've found that several of the built-in templates need a bit of work
to be useful with our JIRA installation (eg #24), so this allows for admins
to make a site-specific set of defaults easily.
2016-03-30 20:20:55 +01:00
coryb 95403a4948 Merge pull request #38 from jglick/patch-1
Noting jira login
2016-03-30 08:48:21 -07:00
Jesse Glick e5053e88cd Noting jira login 2016-03-30 09:39:49 -04:00
coryb 964cf6b4c0 Merge pull request #37 from tobyjoe/add-resource-expansion
Added support for the ```expand``` option for Issues
2016-02-22 09:04:54 -08:00
tobyjoe fb4afc971a Added support for the ``expand`` option for Issues
The ```expand``` option is used to specify resource expansion in the
Jira REST API.

It's particularly useful for things like fetching the ```changelog``` of
an Issue.

This PR adds the support to the ```ListIssues``` and ```ViewIssue```
functions of the ```jira.Cli``` struct.

I'm happy to add tests, but there is currently no test suite in the
master branch, so I did not want to bog down the PR with tangential features.
2016-02-22 08:46:08 -05:00
Cory Bennett 7bfc6e810c change for api changes to go-logging 2016-02-11 15:11:06 -08:00
coryb 7ef8c4fe63 fix #36, build instructions needed updating 2016-02-10 17:03:27 -08:00
coryb b86921351a Merge pull request #35 from QuinnyPig/fix-readme
Fix path for installation instructions
2016-02-02 12:06:48 -08:00
Corey Quinn 4c51e5b1a6 Fix path for command 2016-02-02 11:19:54 -08:00
coryb e4fa47d48c Merge pull request #34 from jonathanio/fix/issuetypes-url-escaping
Fix issuetype calls adding URL escaping
2016-02-01 08:46:21 -08:00
Jonathan Wright e4a25e2ec2 Fix issuetype calls adding URL escaping
It is valid within JIRA to have issue types with spaces (for example we use
"Pro-Active Task") however the issuetype variable is not escaped prior to
formatting into the uri string.

This fix imports "net/url" and escapes all inclusions of issuetype into uri.
The only other variables formatted into uri are c.endpoint and issue which
shouldn't contain special characters.
2016-02-01 11:10:30 +00:00
Cory Bennett 47e9467de1 Updated Changelog 2016-01-29 09:04:49 -08:00
Cory Bennett a573c4e68e Merge branch 'master' of github.com:Netflix-Skunkworks/go-jira 2016-01-29 09:02:39 -08:00
coryb eb096f4cb3 Merge pull request #33 from mikepea/make_jirad
Fixes #32 - make path to cookieFile if it's not present
2016-01-29 09:01:29 -08:00
Mike Pountney 66445793e6 Fixes #32 - make path to cookieFile if it's not present
TL;DR, this ensures ~/jira.d is present, with 0755 perms.

If ~/jira.d isn't present, we can't write to the cookieFile, which
breaks CmdLogin. This is particularly an issue when using /etc/go-jira.yml
to get an entire team using go-jira easily :)

This fixes this by ensuring the cookieFile dir is present before
writing to it.
2016-01-29 11:12:25 +00:00
coryb 8190ad9925 Merge pull request #31 from mikepea/component_mgmt
Add component/components support: add and list for now.
2016-01-28 16:46:06 -08:00
Cory Bennett 2ec88ce5b0 Merge branch 'master' of github.com:Netflix-Skunkworks/go-jira 2016-01-28 16:42:08 -08:00
Mike Pountney d7b32269e1 Add component/components support: add and list for now.
This adds the basics of the component API:

* listing of components assigned to a project
* adding new components to a project.

The interface to 'add component' is similar to that of 'labels', as
previously discussed. We can implement remove/update later.
2016-01-29 00:22:26 +00:00
coryb cb67107a3e Merge pull request #30 from mikepea/unwatch_support
Support for removing a given watcher
2016-01-23 22:54:05 -08:00
coryb 3bf28d28e1 Merge pull request #26 from mikepea/vote_support
Add 'vote' and 'unvote'
2016-01-23 22:52:31 -08:00
Mike Pountney 383847a9d6 Tweak the CmdWatch contract and add watcher remove support
This adjusts the CmdWatch interface as per discussion in
https://github.com/Netflix-Skunkworks/go-jira/pull/26

It also exposes public versions of the c.getOptString and c.getOptBool
utility functions, again as discussed.

The interface to CmdWatch now includes the user to be watched (rather than
depending on the opt[] map. This makes CmdWatch more useful externally.

A '--remove' option has been created, to allow for removal of a given watcher.
This was deliberately not included in the defaults map, as it is specifically only
used for 'watch' command right now. It should be moved up to a default if it becomes
a more common option, I guess (as 'remove is false' isn't a bad default)
2016-01-24 03:00:39 +00:00
Mike Pountney 797edefc3f Amend vote/unvote to be vote/vote --down
This simplifies the interface to voting, as per the discussion in
https://github.com/Netflix-Skunkworks/go-jira/pull/26

Basically, DRY out the logic into a single CmdVote function based
around an 'up' bool. Similarly, make a single CLI command with an
option to do the downvote.
2016-01-24 02:23:08 +00:00
Mike Pountney 7b651d73c2 Merge branch 'master' into vote_support 2016-01-23 18:06:16 +00:00
Cory Bennett f6612c094b update version tag to prefix with v 2016-01-21 17:38:50 -08:00
Cory Bennett b0ed964e3e Updated Changelog 2016-01-21 17:36:23 -08:00
Cory Bennett ee0e780fa4 [issue #28] check to make sure we got back issuetypes for create metadata 2016-01-21 17:35:18 -08:00
Cory Bennett c5fe9f5383 gofmt 2016-01-21 10:52:15 -08:00
coryb 3f20139b75 Merge pull request #27 from blalor/insecure-skip-verify
Add insecure option for TLS endpoints
2016-01-21 10:15:29 -08:00
Brian Lalor 6a88bb9c00 Add insecure option for TLS endpoints
This gives the option of disabling TLS certificate verification for
the server.

Closes #25
2016-01-21 12:30:23 -05:00
Mike Pountney c95e66e081 Add 'vote' and 'unvote'
This adds support for voting on issues via CmdVote() and CmdUnvote()

Voting on issues is always done as the logged in user, it appears you
can't case a vote for another user:

https://docs.atlassian.com/jira/REST/latest/#api/2/issue-addVote

This required adding a cli.delete() handler, naturally with no content
(as per RFC2616)

This is ripe for DRY-ing out, but I will leave that for a future PR.

Worth noting is that you cannot vote for your own issues, this results in:

    2016-01-13T21:35:41.315Z ERROR [cli.go:184] response status: 404 Not Found
    2016-01-13T21:35:41.315Z ERROR [commands.go:439] Unexpected Response From POST:
    {snip}
    {"errorMessages":["You cannot vote for an issue you have reported."],"errors":{}}
2016-01-13 21:41:34 +00:00
coryb 6734532719 Merge pull request #21 from mikepea/label_command
Add 'labels' command to set/add/remove labels
2016-01-04 08:50:15 -08:00
Mike Pountney a637b43f0e Reporter is not generally editable.
Support the lowest common denominator for default_edit_template, as per #23

`reporter` is not usually editable by unprivileged users. Even if it is left as-is,
the PUT request is trying to update it, resulting in the following error:

    {"errorMessages":[],"errors":{"reporter":"Field 'reporter' cannot be set. It is not on the appropriate screen, or unknown."}}

Commenting out the field leaves the information visible, so is a reasonable compromise.
2016-01-03 22:25:05 -08:00
Mike Pountney 303784fd56 Correct naming of parameter: set/add/remove are actions.
'command' is not approprirate for the set/add/remove operations, and is
confusing. Rename to 'action' to remove this confusion.
2016-01-03 20:28:02 -08:00
Mike Pountney 40a7c65366 Tweak CmdLabels args so that magic happens with CLI
The CLI looks for commands (like 'labels') in the first two positional args. This
is how commands like 'BLOCKER blocks ISSUE' work.

As per @coryb's comments in #21 - this allows us to support set/add/remove in a
consistent way for other types: components for example.

This commit rearranges the command to be as follows:

    jira (set/add/remove) labels ISSUE LABELS...

The options to CmdLabels have been reordered accordingly.
2016-01-03 20:10:49 -08:00
Mike Pountney 4f988b42f8 Merge branch 'master' into label_command 2016-01-03 19:50:47 -08:00
coryb 8712b4af6b Merge pull request #22 from mikepea/library_break_out
Expose key functionality for library consumption
2015-12-31 12:22:09 -08:00
Mike Pountney 8977f3dd9b Expose ViewTicket as per FindIssues
This allows external users of the API to retreive issue details, in the same
manner that FindIssues provides for lists of issues.
2015-12-31 12:06:04 -08:00
Mike Pountney da6cbd5021 Add exposed versions of getTemplate and runTemplate
GetTemplate and RunTemplate allow external users to access the template system,
and in particular allow them to provide their own Buffer to write the output to.

I've implemented exposed versions calling the private functions, rather than breaking
the internal API. If this isn't a concern, we should remove getTemplate and runTemplate
in a future commit.
2015-12-31 12:03:22 -08:00
Mike Pountney 408743b3df go fmt 2015-12-31 11:52:55 -08:00
Mike Pountney 230b52d528 Add 'labels' command to set/add/remove labels
This adds 'labels' command, which allows for the setting, addition and removal
of labels on an issue.

'set' action resets the issue labels to the list provided.
'add' action adds the supplied labels to the issue
'remove' action removes the supplied labels from the issue

The API already gracefully handles duplication, removal of non-existant labels, and the
supplying of an empty list of labels (which is useful in the case of 'set')

Eg

jira labels TEST-123 add label1 label2 label3
jira labels TEST-123 remove label1 label2
jira labels TEST-123 set label1 label2 label3
jira labels TEST-123 set # clears any labels on the issue
jira labels TEST-123 add # no-op
jira labels TEST-123 remove # no-op

This mirrors the functionality of the API.
2015-12-24 11:30:52 -08:00
coryb 425b571212 Merge pull request #20 from mikepea/add_join_template_func
Add a 'join' func to the template engine
2015-12-23 12:05:34 -08:00
Mike Pountney a7820fe7d5 Add a 'join' func to the template engine
I needed this so that I can display JIRA labels in my 'list' template.
2015-12-23 06:15:31 -08:00
Cory Bennett 7268b9e346 make "jira" golang package, move code from jira/cli to root, move jira/main.go to main/main.go 2015-12-22 17:56:53 -08:00
Cory Bennett 2df21d3023 Updated Changelog 2015-12-09 20:58:11 -08:00
Cory Bennett 3c30f3b039 fix jira trans TRANS ISSUE (case sensitivity issue), also go fmt 2015-12-09 20:57:10 -08:00
Cory Bennett dd419b161c Updated Changelog 2015-12-03 14:47:55 -08:00
Cory Bennett 4f4a89b972 need to default "quiet" to false 2015-12-03 14:47:31 -08:00
Cory Bennett d8d574a84a Updated Changelog 2015-12-03 12:56:18 -08:00
Cory Bennett c9ac162990 add --quiet command to not print the OK ..
add --saveFile option to print the issue/link to a file on create command
2015-12-03 12:55:14 -08:00
Cory Bennett eaddfe6788 fix overrides 2015-12-03 12:21:55 -08:00
Cory Bennett 90ef56accd add abstract request wrapper to allow you to access/process random apis
supported by Jira but not yet supported by go-jira
2015-12-03 11:35:51 -08:00
Cory Bennett 228d7d0803 Updated Changelog 2015-11-23 17:09:17 -08:00
Cory Bennett a1eb4a1e90 jira edit should not require one arguemnt (allow for --query) 2015-11-23 17:08:40 -08:00
Cory Bennett e67ffb632f fix CURVER in case of building from src tar.gz 2015-11-23 14:35:41 -08:00
Cory Bennett 4c8bf5ddff tweak build 2015-11-23 14:23:29 -08:00
Cory Bennett b9c5dad3a1 Updated Changelog 2015-11-23 14:17:41 -08:00
Cory Bennett fd2a2fe65f [#17] print usage on missing arguments 2015-11-23 14:17:09 -08:00
Cory Bennett 7662885d0d bump version 2015-11-17 12:30:11 -08:00
Cory Bennett 8a7cd2275f Updated Changelog 2015-11-17 12:30:11 -08:00
coryb 1133dae127 Merge pull request #16 from oschrenk/fix-typo
s/enpoint/endpoint/g
2015-10-16 08:50:52 -07:00
Oliver Schrenk c5d251dec8 s/enpoint/endpoint/g 2015-10-16 11:12:25 +02:00
coryb 83da78dfdc Merge pull request #14 from mikepea/ls_with_updated
Add 'updated' to queryfields; dateFormat template command; bug fix.
2015-10-15 08:37:47 -07:00
Mike Pountney 68d3baeac6 Implement dateFormat template command
Wrapper around time.Format, so that we can make concise lists without
the whole JIRA ISO timestamp, eg:

{{ dateFormat "2006-01-02T15:04" .fields.updated }}
2015-10-15 12:43:03 +01:00
Mike Pountney 91e24759e0 Add 'updated' field to default queryfields.
This is pretty essential if you want to get an idea of how stale a ticket is.
2015-10-15 12:35:07 +01:00
Mike Pountney 4d7fdb87a9 Fix export-templates option (typo) 2015-10-15 12:05:33 +01:00
Cory Bennett 47ced2fa24 when yaml element resolves to "\n" strip it out so we dont post it to jira 2015-10-06 11:54:05 -07:00
Cory Bennett 618f245c35 print PUT/POST data when using --dryrun to help debug 2015-10-06 11:50:47 -07:00
Cory Bennett 9d48ca222a bump version 2015-09-19 00:17:32 -07:00
Cory Bennett 6f3dba4e43 Updated Changelog 2015-09-19 00:17:32 -07:00
Cory Bennett 909eb06364 replace dead/deprecated code.google.com/p/gopass with golang.org/x/crypto/ssh/terminal for reading password from stdin 2015-09-19 00:17:08 -07:00
Cory Bennett 3977e530f7 bump version 2015-09-18 23:26:16 -07:00
Cory Bennett 52b1126b28 Updated Changelog 2015-09-18 23:26:16 -07:00
Cory Bennett 9348a4b4c0 fix exception from "jira create" 2015-09-18 23:24:50 -07:00
Cory Bennett 1c08a7d609 add some debug messages to help diagnose login failures 2015-09-18 23:18:38 -07:00
Cory Bennett bb9097b3ec bump version 2015-09-16 23:22:06 -07:00
Cory Bennett 355d35ff58 Updated Changelog 2015-09-16 23:22:06 -07:00
Cory Bennett 0ae6a0790a update version when we create changelog entries 2015-09-16 23:21:36 -07:00
Cory Bennett 8385ee2afb add --version 2015-09-16 23:16:51 -07:00
Cory Bennett 23347ddcad Updated Changelog 2015-09-16 23:05:02 -07:00
Cory Bennett 15ae929703 fix command line parser broken in 0.0.10 2015-09-16 23:04:36 -07:00
Cory Bennett e2c27f18d2 update changelog 2015-09-15 23:43:57 -07:00
Cory Bennett 2ee842270a Updated Changelog 2015-09-15 23:41:20 -07:00
Cory Bennett 07e3f7bea6 gofmt 2015-09-15 23:40:41 -07:00
Cory Bennett 67f30f7d46 update usage 2015-09-15 23:29:56 -07:00
Cory Bennett 23590d4173 allow for command aliasing in conjunction with executable config files 2015-09-15 23:27:06 -07:00
Cory Bennett ef7a57ec24 update usage 2015-09-15 22:55:19 -07:00
Cory Bennett 4a19089dd7 Updated Changelog 2015-09-15 20:34:19 -07:00
Cory Bennett f84e77fd84 * use forked yaml.v2 so as to not lose line terminations present in jira fields
* adding a |~ literal yaml syntax to just chomp a single newline (again to preserve
existing formatting in jira fields)
* for indent/comment allow for unicode line termination characters that yaml will use for parsing
2015-09-15 19:31:38 -07:00
Cory Bennett 4265913001 fix "edit" default option, change how defaults are dealt with for filters 2015-09-14 12:31:53 -07:00
Cory Bennett 968a9df71f for edit template add issue id as comment, also add "comments" as comment
so you can review the comment details while editing
2015-09-14 12:30:26 -07:00
Cory Bennett d6648687fb add "comment" template filter to comment out multiline statements 2015-09-14 12:29:30 -07:00
Cory Bennett c0070cfd58 add getOpt wrappers to get options with defaults 2015-09-14 12:29:05 -07:00
Cory Bennett d229ac1156 make --dryrun work
refactor config/option loading so command options override settings in config files
allow query options to be used on the "edit" command to iterate editing
2015-09-14 00:14:13 -07:00
Cory Bennett f8c8ddf78e move remove duplication for defaults 2015-09-13 17:31:28 -07:00
Cory Bennett 39318ebf75 adding "clean" target 2015-09-13 17:13:55 -07:00
Cory Bennett 7bbd571078 use optigo for option parsing, drop docopt 2015-09-13 17:00:03 -07:00
Cory Bennett ea67a77863 allow "abort: true" to be set while editing to cancel the edit operation 2015-09-11 23:47:09 -07:00
Cory Bennett 99f62a9e99 gofmt 2015-09-11 22:13:23 -07:00
Cory Bennett 85f617c13c tweak changelog generation 2015-09-11 22:13:12 -07:00
Cory Bennett 0a4d6aece8 fix missing arg of Sprintf 2015-09-11 22:12:38 -07:00
Cory Bennett e69b65cd7d if no changes are made on edit templates then abort edit 2015-09-11 22:04:02 -07:00
Cory Bennett 0ede380834 updated for 0.0.8 2015-09-11 21:18:27 -07:00
Cory Bennett faf0793312 bump version 2015-07-31 11:12:24 -07:00
Cory Bennett eeb63441dc update for max_results option 2015-07-31 11:11:36 -07:00
coryb 8d3a6ddd80 Merge pull request #11 from mikepea/max_results_option
Add --max_results option for 'ls'
2015-07-31 10:37:04 -07:00
Mike Pountney e06ff0cc54 Add --max_results option for 'ls'
This closes #10

Shifts the hardcoded maxResults value for the cmdList json body
into a 'max_results' option.

Note that testing against our JIRA instance, in a project with
more than 1000 open issues, suggests that the JIRA has an internal
limit of 1000 results in a single query.
2015-07-31 17:38:45 +01:00
Cory Bennett cfd09c306b update Changelog 2015-07-01 10:22:06 -07:00
Cory Bennett 6374897b58 fix the "all" build 2015-07-01 10:20:48 -07:00
Cory Bennett 4f45159dbf add cross-compile setup task 2015-07-01 09:13:03 -07:00
Cory Bennett c3d62c0fa1 udpate version 2015-07-01 09:12:40 -07:00
Cory Bennett b9e5564dff Merge branch 'master' of github.com:Netflix-Skunkworks/go-jira 2015-07-01 08:50:40 -07:00
coryb e8631c6b6d Merge pull request #9 from nelfin/quickfix/take-user
fix "take" command not honouring user option
2015-06-30 23:29:09 -07:00
Andrew Haigh 8f1d2b9f76 fix "take" command not honouring user option
"take" was simply a partial on "assign", but accidentally used the value
of $USER from the environment rather than `opts["user"]`, preventing the
user from overriding this value in config.yml or as a command-line
argument.
2015-07-01 16:21:26 +10:00
Cory Bennett 06f57fea5b fix typo 2015-03-02 14:54:24 -08:00
Cory Bennett 39b14b7ff1 strip binaries when building "all" 2015-03-02 14:34:38 -08:00
Cory Bennett 2d2e5e26d0 updating 2015-02-27 17:52:07 -08:00
Cory Bennett 7b2122742b bump version 2015-02-27 17:50:08 -08:00
Cory Bennett 701f091668 allow --sort= to disable sort override 2015-02-24 17:49:21 -08:00
Cory Bennett 82fd9b9ef3 fix default JIRA_OPERATION env variable 2015-02-24 17:48:59 -08:00
Cory Bennett ebf170085c automatically close duplicate issues with "Duplicate" resolution 2015-02-23 12:09:40 -08:00
Cory Bennett 050848adea set JIRA_OPERATION to "view" when no operation used (ie: jira GOJIRA-123) 2015-02-23 12:08:54 -08:00
Cory Bennett f35903005c add --sort option to "list" command 2015-02-23 12:08:33 -08:00
Cory Bennett 9531f3fef1 adding changlog 2015-02-21 23:36:42 -08:00
Cory Bennett 093a58de12 bump version 2015-02-21 23:27:53 -08:00
Cory Bennett ec221cb41a fix typo 2015-02-21 21:35:31 -08:00
Cory Bennett f4b5428a37 update README.md 2015-02-21 21:32:42 -08:00
Cory Bennett 7186fb3e3e handle editor having arguments 2015-02-21 21:15:02 -08:00
Cory Bennett 3e6f2b396b add more template error handling 2015-02-20 15:03:40 -08:00
Cory Bennett d9e4db2f7a gofmt 2015-02-20 14:39:01 -08:00
Cory Bennett e59cd45c2d gofmt 2015-02-20 14:37:55 -08:00
Cory Bennett 4db2e4e8b4 allow create template to specify defalt watchers with -o watchers=... 2015-02-20 14:29:33 -08:00
Cory Bennett affd313cf7 update README.md 2015-02-20 14:29:19 -08:00
Cory Bennett 7a2f7f5726 if config files are executable then run them and parse the output 2015-02-20 13:37:24 -08:00
Cory Bennett c7f1133500 update usage 2015-02-19 18:03:33 -08:00
Cory Bennett 1d76e8f289 bump version 2015-02-19 13:09:32 -08:00
Cory Bennett 343fbb6e56 add --template option to export-templates to export a single template 2015-02-19 13:07:46 -08:00
Cory Bennett 63a1fd9c60 update for table template 2015-02-19 12:38:12 -08:00
Cory Bennett 8954ec1cfa add "table" template to be used with "list" command 2015-02-19 12:36:51 -08:00
Cory Bennett 21d3c884bc prevent line wrapping 2015-02-19 11:17:33 -08:00
Cory Bennett d5330fd78e bump version 2015-02-19 10:31:20 -08:00
Cory Bennett 2dcf665d38 [issue #8] detect X-Seraph-Loginreason: AUTHENTICATION_DENIED header to catch login failures 2015-02-19 10:05:51 -08:00
coryb 4c2d77ccca Merge pull request #7 from jaybuff/empty-projects
validate project
2015-02-18 21:06:55 -08:00
Jay Buffington 1b69d126c0 project should always be uppercase
Jira docs say as much:
https://confluence.atlassian.com/display/JIRA/Changing+the+Project+Key+Format#ChangingtheProjectKeyFormat-prerequisites
2015-02-18 19:26:36 -08:00
Jay Buffington 4924dfaab3 if response is 400, check json for errorMessages and log them 2015-02-18 17:39:37 -08:00
Jay Buffington dc5ae42c58 validate project 2015-02-18 17:32:56 -08:00
Cory Bennett 93ef7a2a3e bump version 2015-02-18 15:02:46 -08:00
Cory Bennett 56e3cec6d4 adding simple Makefile to make cross compiling a bit easier 2015-02-18 15:01:47 -08:00
Cory Bennett 13d77c1b4d adding missing --override arguments to various transition commands
add browse command
2015-02-18 15:01:12 -08:00
coryb 4aeba2e34e Update README.md 2015-02-18 14:43:20 -08:00
coryb 4152c37335 Update README.md 2015-02-18 14:38:47 -08:00
coryb 97fccbddeb Update README.md 2015-02-18 13:38:46 -08:00
coryb 2ac1a3e7f2 Update README.md 2015-02-18 13:36:28 -08:00
coryb 0edb1a354c Update README.md 2015-02-18 13:35:53 -08:00
Cory Bennett 8f1e7b6619 add download link 2015-02-18 13:29:20 -08:00
Cory Bennett 646020f8f5 update usage in readme 2015-02-18 11:38:12 -08:00
Cory Bennett 484135e6eb fill in issuetype before running the "create" template 2015-02-18 11:34:24 -08:00
Cory Bennett f7ab5753e9 minor tweak to prevent exceptions in some templates
set default for "commit" command to use editor
2015-02-17 16:03:32 -08:00
Cory Bennett 005607d799 added created field to "view" template 2015-02-17 11:09:24 -08:00
Cory Bennett 165e1144ed fix view template for empty description 2015-02-17 10:40:11 -08:00
Cory Bennett 68644878fc add "transition" editable template so you can modify other fields during transitions
add --noedit/--edit options for various commands
sanitize the yaml from the editor so we dont send empty strings when unedited
2015-02-17 10:19:54 -08:00
Cory Bennett 2baf87af9f add --watcher and --reporter options to "list" 2015-02-16 15:27:47 -08:00
Cory Bennett 766923be02 dont write response to log unless request, otherwise response body will have
been read and unavailable to parse later in client code
2015-02-16 15:19:25 -08:00
Cory Bennett 70fcdd8ef4 [issue #4] make sure ~/.jira.d/tmp exists before we write to it during create/edit 2015-02-16 14:31:39 -08:00
Cory Bennett 018f12e3c9 add --queryfields option to restrict data returned from jira search api. This is for performance
as the jira issue data can be quite large and we use almost none of it in the "list" template
2015-02-16 14:06:36 -08:00
Cory Bennett 12fc734069 dont default issuetype unless it is a require param, otherwise "ls" is restricted 2015-02-16 13:21:15 -08:00
Cory Bennett 21d9f74635 adding --browse option to most command which should open the issue in your favorite browser 2015-02-16 10:34:04 -08:00
Cory Bennett b7bdb43471 Merge branch 'jaybuff-nil-assignee' 2015-02-16 09:51:43 -08:00
Cory Bennett 961ac38a08 fix inner assignee template 2015-02-16 09:51:22 -08:00
Cory Bennett 172ac70046 Merge branch 'nil-assignee' of https://github.com/jaybuff/go-jira into jaybuff-nil-assignee 2015-02-16 09:50:57 -08:00
Cory Bennett f206ae3ba2 fix indent for override coments in edit template 2015-02-16 08:53:37 -08:00
coryb 52966e0aa8 Merge pull request #2 from jaybuff/clean-up
Clean up
2015-02-16 08:50:36 -08:00
Jay Buffington 7d25d7715f default view template shouldn't fail when assignee is nil
The default view template was failing with this error whenever I tried to
view a jira that had no assignee:
assignee: 2015-02-16T08:31:58.564-08:00 ERROR [util.go:109] Failed to execute template: template: template:7:20: executing "template" at <.fields.assignee.nam...>: nil pointer evaluating interface {}.name
2015-02-16 08:48:10 -08:00
Jay Buffington 30e2237b6a clean up README (mostly spelling fixes) 2015-02-16 08:44:12 -08:00
Jay Buffington 88af72543a add .gitignore 2015-02-16 08:32:18 -08:00
Cory Bennett 3a0867d0ed [issue #1] trim trailing / on uri 2015-02-15 21:02:28 -08:00
Cory Bennett deb3a7255f . 2015-02-13 16:58:57 -08:00
Cory Bennett 51e0a341e8 update for golang minver 2015-02-13 16:58:26 -08:00
Cory Bennett c64f5cf44e udpate usage 2015-02-13 16:48:57 -08:00
Cory Bennett 1054d506b3 tweak usage ordering
fix looking for generic "create" template
2015-02-13 16:48:10 -08:00
Cory Bennett 6f3e25d1a7 udpate Editor section 2015-02-13 16:48:03 -08:00
Cory Bennett 8557c4f384 update 2015-02-13 16:38:20 -08:00
Cory Bennett 1cff26364b dont default endpoint make users specify it on command line or in config 2015-02-13 16:38:01 -08:00
Cory Bennett 5422bfb152 tweak getTemplate routine 2015-02-13 16:03:03 -08:00
Cory Bennett a872b66777 go fmt 2015-02-13 15:44:07 -08:00
Cory Bennett eb0c0823b0 adding export-templates command
updated edit template to allow for -o overrides
2015-02-13 14:07:20 -08:00
Cory Bennett 9c890a02a8 sort usage options, update README 2015-02-13 13:19:10 -08:00
Cory Bennett 618af1a6e1 add options to ls to allow for dynamically creating some simple JQL 2015-02-13 13:16:18 -08:00
Cory Bennett e433c536c0 add take/give commands 2015-02-13 12:52:35 -08:00
Cory Bennett 4a33dd8e20 adding "comment" command 2015-02-13 12:33:47 -08:00
Cory Bennett 0b5e00d2e3 handle various issue state transitions 2015-02-13 12:07:03 -08:00
Cory Bennett d6ad05ac9c adding commands:
* create
* dups
* blocks
* watch
2015-02-12 23:41:39 -08:00
Cory Bennett 081fda79f3 work in progress, minor refactor. Added commands:
* login
* editmeta ISSUE
* edit ISSUE
* issuetypes [-p PROJECT]
* createmeta [-p PROJECT] [-i ISSUETYPE]
* transitions ISSUE

make --template argumetn work
2015-02-12 15:50:08 -08:00
Cory Bennett df67697fb7 need to set GOBIN for install to work 2015-02-10 16:31:48 -08:00
Cory Bennett e6b113d3fb update readme, change config location 2015-02-10 16:27:16 -08:00
Cory Bennett 477c3e980c initial checkin, work in progress 2015-02-10 16:17:13 -08:00
74 changed files with 10495 additions and 1382 deletions
+9
View File
@@ -5,3 +5,12 @@ src/github.com/docopt/
src/github.com/mgutz/
src/github.com/op/
src/gopkg.in/
jira
jira.exe
schemas/*.json
t/issue.props
t/.jira.d/templates
dist/
src/
t/.maven-cache
vendor/
+22
View File
@@ -0,0 +1,22 @@
# docker required for the tests (run via prove)
sudo: required
services:
- docker
language: go
go:
- 1.6
matrix:
fast_finish: true
script:
- make vet
- make lint
- make
- prove -v
cache:
directories:
- t/.maven-cache
+122
View File
@@ -1,5 +1,127 @@
# Changelog
## 0.1.7 - 2016-08-24
* Prefer transition names which match exactly [Don Brower] [[e40f9c1](https://github.com/Netflix-Skunkworks/go-jira/commit/e40f9c1)]
* update tempates to make them more readable with space trimming added to go-1.6 [Cory Bennett] [[693b3e4](https://github.com/Netflix-Skunkworks/go-jira/commit/693b3e4)]
## 0.1.6 - 2016-08-21
* make "worklogs" command print output through template allow "add worklog" command to open edit template [Cory Bennett] [[cc3fbee](https://github.com/Netflix-Skunkworks/go-jira/commit/cc3fbee)]
* remove extra newline at end of worklogs template [Cory Bennett] [[d08ef15](https://github.com/Netflix-Skunkworks/go-jira/commit/d08ef15)]
* adding worklog related templates [Cory Bennett] [[ab1cd27](https://github.com/Netflix-Skunkworks/go-jira/commit/ab1cd27)]
## 0.1.5 - 2016-08-21
* update for golint [Cory Bennett] [[5a4e17c](https://github.com/Netflix-Skunkworks/go-jira/commit/5a4e17c)]
* fix for go vet [Cory Bennett] [[355fb42](https://github.com/Netflix-Skunkworks/go-jira/commit/355fb42)]
## 0.1.4 - 2016-08-12
* when running "dups" on a Process Management Project type, you have to start/stop the task to resolve it [Cory Bennett] [[2c91905](https://github.com/Netflix-Skunkworks/go-jira/commit/2c91905)]
* allow for defaultResolution option for transition command [Cory Bennett] [[a328c2d](https://github.com/Netflix-Skunkworks/go-jira/commit/a328c2d)]
* add "backlog" command for Kanban related Issues [Cory Bennett] [[5d39b23](https://github.com/Netflix-Skunkworks/go-jira/commit/5d39b23)]
* fix --noedit flag with "dups" command [Cory Bennett] [[37c07fa](https://github.com/Netflix-Skunkworks/go-jira/commit/37c07fa)]
* add "votes" and "labels" to default view template [Cory Bennett] [[6f73b8c](https://github.com/Netflix-Skunkworks/go-jira/commit/6f73b8c)]
* add "blockerType" config param, for issueLinkType use for "blocks" command [Cory Bennett] [[30fd301](https://github.com/Netflix-Skunkworks/go-jira/commit/30fd301)]
* update gitter room [Cory Bennett] [[4b822b1](https://github.com/Netflix-Skunkworks/go-jira/commit/4b822b1)]
* default issuetype to "Bug" for project that have Bug, otherwise try "Task" [Cory Bennett] [[0c807b4](https://github.com/Netflix-Skunkworks/go-jira/commit/0c807b4)]
* make view template only show fields that have values [Cory Bennett] [[8238fe8](https://github.com/Netflix-Skunkworks/go-jira/commit/8238fe8)]
* make default create template only display fields if they are valid fields for the project [Cory Bennett] [[adc2ace](https://github.com/Netflix-Skunkworks/go-jira/commit/adc2ace)]
* ignore empty json fields when processing templates [Cory Bennett] [[f5f3e28](https://github.com/Netflix-Skunkworks/go-jira/commit/f5f3e28)]
* allow JIRA_LOG_FORMAT env variable to control log output format [Cory Bennett] [[469def0](https://github.com/Netflix-Skunkworks/go-jira/commit/469def0)]
* remove extraneous debug [Cory Bennett] [[752a94d](https://github.com/Netflix-Skunkworks/go-jira/commit/752a94d)]
* add logout command modify password prompt to echo masked password [Cory Bennett] [[8ad91be](https://github.com/Netflix-Skunkworks/go-jira/commit/8ad91be)]
* tweak cookies to store hostname dump all http request/response with --verbose [Cory Bennett] [[f93fe79](https://github.com/Netflix-Skunkworks/go-jira/commit/f93fe79)]
* load configs in order of closest to cwd (/etc/go-jira.yml is last) [Cory Bennett] [[f54267b](https://github.com/Netflix-Skunkworks/go-jira/commit/f54267b)]
## 0.1.3 - 2016-07-30
* [[#43](https://github.com/Netflix-Skunkworks/go-jira/issues/43)] add support for jira done|todo|prog commands [Cory Bennett] [[dd7d1cc](https://github.com/Netflix-Skunkworks/go-jira/commit/dd7d1cc)]
* Reporter is not generally editable. [Mike Pountney] [[a637b43](https://github.com/Netflix-Skunkworks/go-jira/commit/a637b43)]
## 0.1.2 - 2016-06-29
* [[#44](https://github.com/Netflix-Skunkworks/go-jira/issues/44)] Close tmpfile before rename to work around "The process cannot access the file because it is being used by another process" error on windows. [Cory Bennett] [[0980f8e](https://github.com/Netflix-Skunkworks/go-jira/commit/0980f8e)]
## 0.1.1 - 2016-06-28
* use USERPROFILE instead of HOME for windows, rework paths to use filepath.Join for better cross platform support [Cory Bennett] [[adcedc4](https://github.com/Netflix-Skunkworks/go-jira/commit/adcedc4)]
* Include templates from a system path [Mike Pountney] [[cf10f53](https://github.com/Netflix-Skunkworks/go-jira/commit/cf10f53)]
* Added support for the ```expand``` option for Issues [tobyjoe] [[fb4afc9](https://github.com/Netflix-Skunkworks/go-jira/commit/fb4afc9)]
* change for api changes to go-logging [Cory Bennett] [[7bfc6e8](https://github.com/Netflix-Skunkworks/go-jira/commit/7bfc6e8)]
* Fix issuetype calls adding URL escaping [Jonathan Wright] [[e4a25e2](https://github.com/Netflix-Skunkworks/go-jira/commit/e4a25e2)]
## 0.1.0 - 2016-01-29
* Fixes [#32](https://github.com/Netflix-Skunkworks/go-jira/issues/32) - make path to cookieFile if it's not present [Mike Pountney] [[6644579](https://github.com/Netflix-Skunkworks/go-jira/commit/6644579)]
* Add component/components support: add and list for now. [Mike Pountney] [[d7b3226](https://github.com/Netflix-Skunkworks/go-jira/commit/d7b3226)]
* Tweak the CmdWatch contract and add watcher remove support [Mike Pountney] [[383847a](https://github.com/Netflix-Skunkworks/go-jira/commit/383847a)]
* Amend vote/unvote to be vote/vote --down [Mike Pountney] [[797edef](https://github.com/Netflix-Skunkworks/go-jira/commit/797edef)]
* Add 'vote' and 'unvote' [Mike Pountney] [[c95e66e](https://github.com/Netflix-Skunkworks/go-jira/commit/c95e66e)]
## 0.0.20 - 2016-01-21
* [issue [#28](https://github.com/Netflix-Skunkworks/go-jira/issues/28)] check to make sure we got back issuetypes for create metadata [Cory Bennett] [[ee0e780](https://github.com/Netflix-Skunkworks/go-jira/commit/ee0e780)]
* Add insecure option for TLS endpoints [Brian Lalor] [[6a88bb9](https://github.com/Netflix-Skunkworks/go-jira/commit/6a88bb9)]
* Correct naming of parameter: set/add/remove are actions. [Mike Pountney] [[303784f](https://github.com/Netflix-Skunkworks/go-jira/commit/303784f)]
* Tweak CmdLabels args so that magic happens with CLI [Mike Pountney] [[40a7c65](https://github.com/Netflix-Skunkworks/go-jira/commit/40a7c65)]
* Expose ViewTicket as per FindIssues [Mike Pountney] [[8977f3d](https://github.com/Netflix-Skunkworks/go-jira/commit/8977f3d)]
* Add exposed versions of getTemplate and runTemplate [Mike Pountney] [[da6cbd5](https://github.com/Netflix-Skunkworks/go-jira/commit/da6cbd5)]
* Add 'labels' command to set/add/remove labels [Mike Pountney] [[230b52d](https://github.com/Netflix-Skunkworks/go-jira/commit/230b52d)]
* Add a 'join' func to the template engine [Mike Pountney] [[a7820fe](https://github.com/Netflix-Skunkworks/go-jira/commit/a7820fe)]
* make "jira" golang package, move code from jira/cli to root, move jira/main.go to main/main.go [Cory Bennett] [[7268b9e](https://github.com/Netflix-Skunkworks/go-jira/commit/7268b9e)]
## 0.0.19 - 2015-12-09
* fix jira trans TRANS ISSUE (case sensitivity issue), also go fmt [Cory Bennett] [[3c30f3b](https://github.com/Netflix-Skunkworks/go-jira/commit/3c30f3b)]
## 0.0.18 - 2015-12-03
* need to default "quiet" to false [Cory Bennett] [[4f4a89b](https://github.com/Netflix-Skunkworks/go-jira/commit/4f4a89b)]
## 0.0.17 - 2015-12-03
* add --quiet command to not print the OK .. add --saveFile option to print the issue/link to a file on create command [Cory Bennett] [[c9ac162](https://github.com/Netflix-Skunkworks/go-jira/commit/c9ac162)]
* fix overrides [Cory Bennett] [[eaddfe6](https://github.com/Netflix-Skunkworks/go-jira/commit/eaddfe6)]
* add abstract request wrapper to allow you to access/process random apis supported by Jira but not yet supported by go-jira [Cory Bennett] [[90ef56a](https://github.com/Netflix-Skunkworks/go-jira/commit/90ef56a)]
## 0.0.16 - 2015-11-23
* jira edit should not require one arguemnt (allow for --query) [Cory Bennett] [[a1eb4a1](https://github.com/Netflix-Skunkworks/go-jira/commit/a1eb4a1)]
## 0.0.15 - 2015-11-23
* [[#17](https://github.com/Netflix-Skunkworks/go-jira/issues/17)] print usage on missing arguments [Cory Bennett] [[fd2a2fe](https://github.com/Netflix-Skunkworks/go-jira/commit/fd2a2fe)]
## 0.0.14 - 2015-11-17
* s/enpoint/endpoint/g [Oliver Schrenk] [[c5d251d](https://github.com/Netflix-Skunkworks/go-jira/commit/c5d251d)]
* Implement dateFormat template command [Mike Pountney] [[68d3bae](https://github.com/Netflix-Skunkworks/go-jira/commit/68d3bae)]
* Add 'updated' field to default queryfields. [Mike Pountney] [[91e2475](https://github.com/Netflix-Skunkworks/go-jira/commit/91e2475)]
* Fix export-templates option (typo) [Mike Pountney] [[4d7fdb8](https://github.com/Netflix-Skunkworks/go-jira/commit/4d7fdb8)]
* when yaml element resolves to "\n" strip it out so we dont post it to jira [Cory Bennett] [[47ced2f](https://github.com/Netflix-Skunkworks/go-jira/commit/47ced2f)]
* print PUT/POST data when using --dryrun to help debug [Cory Bennett] [[618f245](https://github.com/Netflix-Skunkworks/go-jira/commit/618f245)]
## 0.0.13 - 2015-09-19
* replace dead/deprecated code.google.com/p/gopass with golang.org/x/crypto/ssh/terminal for reading password from stdin [Cory Bennett] [[909eb06](https://github.com/Netflix-Skunkworks/go-jira/commit/909eb06)]
## 0.0.12 - 2015-09-18
* fix exception from "jira create" [Cory Bennett] [[9348a4b](https://github.com/Netflix-Skunkworks/go-jira/commit/9348a4b)]
* add some debug messages to help diagnose login failures [Cory Bennett] [[1c08a7d](https://github.com/Netflix-Skunkworks/go-jira/commit/1c08a7d)]
## 0.0.11 - 2015-09-16
* add --version [Cory Bennett] [[8385ee2](https://github.com/Netflix-Skunkworks/go-jira/commit/8385ee2)]
* fix command line parser broken in 0.0.10 [Cory Bennett] [[15ae929](https://github.com/Netflix-Skunkworks/go-jira/commit/15ae929)]
## 0.0.10 - 2015-09-15
* allow for command aliasing in conjunction with executable config files. Issue #5 [Cory Bennett] [[23590d4](https://github.com/Netflix-Skunkworks/go-jira/commit/23590d4)]
* update usage [Cory Bennett] [[ef7a57e](https://github.com/Netflix-Skunkworks/go-jira/commit/ef7a57e)]
## 0.0.9 - 2015-09-15
* use forked yaml.v2 so as to not lose line terminations present in jira fields [Cory Bennett] [[f84e77f](https://github.com/Netflix-Skunkworks/go-jira/commit/f84e77f)]
+83 -37
View File
@@ -1,67 +1,113 @@
PLATFORMS= \
freebsd-386 \
freebsd-amd64 \
freebsd-arm \
linux-386 \
linux-amd64 \
linux-arm \
openbsd-386 \
openbsd-amd64 \
windows-386 \
windows-amd64 \
darwin-386 \
darwin-amd64 \
freebsd/amd64 \
linux/386 \
linux/amd64 \
windows/386 \
windows/amd64 \
darwin/amd64 \
$(NULL)
DIST=$(shell pwd)/dist
export GOPATH=$(shell pwd)
NAME=jira
build:
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
go get -v
OS=$(shell uname -s)
ifeq ($(filter CYGWIN%,$(OS)),$(OS))
export CWD=$(shell cygpath -wa .)
export SEP=\\
export CYGWIN=winsymlinks:native
BIN ?= $(GOBIN)$(SEP)$(NAME).exe
else
export CWD=$(shell pwd)
export SEP=/
BIN ?= $(GOBIN)$(SEP)$(NAME)
endif
GOPATH ?= $(CWD)
export GOPATH
DIST=$(CWD)$(SEP)dist
GOBIN ?= $(CWD)
CURVER ?= $(patsubst v%,%,$(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}'))
LDFLAGS := -w
PACKAGE=gopkg.in/Netflix-Skunkworks/go-jira.v1
# use 'make debug' and you can get a debuggable golang binary
# see https://golang.org/doc/gdb
# note on mac's you will need to codesign the gdb binary before you can use it:
# codesign -fs gdb-cert /usr/local/bin/gdb
ifneq ($(DEBUG),)
GOBUILD=go build -ldflags "-s" -gcflags "-N -l"
else
GOBUILD=go build -ldflags "$(LDFLAGS) -s"
endif
build: $(GOPATH)/src/$(PACKAGE)
cd $(GOPATH)/src/$(PACKAGE) && $(GOBUILD) -o $(BIN)
debug:
$(MAKE) DEBUG=1 build
$(GOPATH)/src/%:
mkdir -p $(@D)
test -L $@ || ln -sf ../../.. $@
glide install -v
vet:
@go vet *.go lib/*.go data/*.go
lint:
@go get github.com/golang/lint/golint
@$(GOPATH)/bin/golint .
@$(GOPATH)/bin/golint ./data
@$(GOPATH)/bin/golint ./lib
test: $(GOPATH)/src/$(PACKAGE)
cd $(GOPATH)/src/$(SUBPACKAGE) && go test -v
cross-setup:
for p in $(PLATFORMS); do \
echo "Building for $$p"; \
cd $(GOROOT)/src && sudo GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
echo Building for $$p"; \
cd $(GOROOT)/src && sudo GOROOT_BOOTSTRAP=$(GOROOT) GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
done
all:
rm -rf $(DIST); \
mkdir -p $(DIST); \
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
go get -d; \
for p in $(PLATFORMS); do \
echo "Building for $$p"; \
GOOS=$${p/-*/} GOARCH=$${p/*-/} go build -v -ldflags -s -o $(DIST)/jira-$$p; \
done
all: $(GOPATH)/src/$(PACKAGE)
docker pull karalabe/xgo-latest
rm -rf dist
mkdir -p dist
docker run --rm -e EXT_GOPATH=/gopath -v $(GOPATH):/gopath -e TARGETS="$(PLATFORMS)" -v $$(pwd)/dist:/build karalabe/xgo-latest $(PACKAGE)
cd $(DIST) && for x in go-jira-*; do mv $$x $$(echo $$x | cut -c 4-); done
fmt:
gofmt -s -w jira
gofmt -s -w main.go lib/*.go data/*.go
install:
export GOBIN=~/bin && ${MAKE} build
${MAKE} GOBIN=$(shell echo ~)/bin build
CURVER ?= $(shell git fetch --tags && git tag | tail -1)
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
TODAY := $(shell date +%Y-%m-%d)
changes:
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^$(CURVER) HEAD jira | grep -v gofmt | grep -v "bump version"
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^v$(CURVER) HEAD *.go lib/*.go data/*.go | grep -vE 'gofmt|go fmt'
update-changelog:
update-changelog:
@echo "# Changelog" > CHANGELOG.md.new; \
echo >> CHANGELOG.md.new; \
echo "## $(NEWVER) - $(TODAY)" >> CHANGELOG.md.new; \
echo >> CHANGELOG.md.new; \
$(MAKE) changes | \
$(MAKE) --no-print-directory --silent changes | \
perl -pe 's{\[([a-f0-9]+)\]}{[[$$1](https://github.com/Netflix-Skunkworks/go-jira/commit/$$1)]}g' | \
perl -pe 's{\#(\d+)}{[#$$1](https://github.com/Netflix-Skunkworks/go-jira/issues/$$1)}g' >> CHANGELOG.md.new; \
tail +2 CHANGELOG.md >> CHANGELOG.md.new; \
tail -n +2 CHANGELOG.md >> CHANGELOG.md.new; \
mv CHANGELOG.md.new CHANGELOG.md; \
git commit -m "Updated Changelog" CHANGELOG.md; \
git tag $(NEWVER)
perl -pi -e 's{VERSION = "$(CURVER)"}{VERSION = "$(NEWVER)"}' lib/cli.go; \
git commit -m "version bump" lib/cli.go; \
git tag v$(NEWVER)
version:
@echo $(CURVER)
clean:
rm -rf pkg dist bin && find src \! -path \*/go-jira\* -delete
rm -rf pkg dist bin src ./$(NAME)
+81 -57
View File
@@ -1,3 +1,8 @@
[![Join the chat at https://gitter.im/go-jira-cli/help](https://badges.gitter.im/go-jira-cli/help.svg)](https://gitter.im/go-jira-cli/help?utm_source=badge&utm_medium=badge&utm_content=badge)
[![Build Status](https://travis-ci.org/Netflix-Skunkworks/go-jira.svg?branch=master)](https://travis-ci.org/Netflix-Skunkworks/go-jira)
[![GoDoc](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v0?status.png)](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v0)
# go-jira
simple command line client for Atlassian's Jira service written in Go
@@ -69,25 +74,13 @@ You can download one of the pre-built binaries for **go-jira** [here](https://gi
* **NOTE** You will need **`go-1.4.1`** minimum
* If you do not have a **GOPATH** setup, these are simple build steps:
* To build the `jira` binary the current directory just run:
```bash
git clone git@github.com:Netflix-Skunkworks/go-jira.git
cd go-jira
export GOPATH=$(pwd)
export GOBIN=$GOPATH/bin
export PATH=$GOBIN:$PATH
cd src/github.com/Netflix-Skunkworks/go-jira/jira
go get -v
make
```
* If you do have a **GOPATH** setup, these are the standard steps to build:
```
cd $GOPATH
git clone git@github.com:Netflix-Skunkworks/go-jira.git src/github.com/Netflix-Skunkworks/go-jira
cd src/github.com/Netflix-Skunkworks/go-jira/jira
go get -v
* To install the binary to you ~/bin directory you can run:
```bash
make install
```
## Configuration
@@ -117,6 +110,8 @@ endpoint: https://jira.mycompany.com
EOM
```
Then use `jira login` to authenticate yourself.
### Dynamic Configuration
If the **.jira.d/config.yml** file is executable, then **go-jira** will attempt to execute the file and use the stdout for configuration. You can use this to customize templates or other overrides depending on what type of operation you are running. For example if you would like to use the "table" template when ever you run `jira ls`, then you can create a template like this:
@@ -167,60 +162,89 @@ When running a command like `jira edit` it will look through the current directo
if found it will use that file as the template, otherwise it will use the default **edit** template hard-coded into **go-jira**. You can export the default
hard-coded templates with `jira export-templates` which will write them to **~/.jira.d/templates/**.
#### Writing/Editing Templates
First the basic templating functionality is defined by the Go language 'text/template' library. The library reference documentation can be found [here](https://golang.org/pkg/text/template/), and there is a good primer document [here](https://gohugo.io/templates/go-templates/). `go-jira` also provides a few extra helper functions to make it a bit easlier to format the data, those functions are defined [here](https://github.com/Netflix-Skunkworks/go-jira/blob/master/util.go#L133).
Knowing what data and fields are available to any given template is not obvious. The easiest approach to determine what is available is to use the `debug` template on any given operation. For eample to find out what is available to the "view" templates, you can use:
```
jira view GOJIRA-321 -t debug
```
This will print out the data in JSON format that is available to the template. You can do this for any other operation, like "list":
```
jira list -t debug
```
Figuring out what is available to input templates (like for the `create` operation) is a bit more tricky, but similar. To find the data available for a `create` template you can run:
```
jira create --dryrun -t debug --editor /bin/cat
```
This will attempt to fetch metadata for your default project (you can provide any options that you would normally specify for the `create` operation). It uses the `--dryrun` option to prevent any actual updates being sent to Jira. The `-t debug` is like before to cause the input to be serialized to JSON and printed for your inspection. Finally the `--editor /bin/cat` will cause `go-jira` to just print the template rather than open up an editor and wait for you to edit/save it.
## Usage
```
Usage:
jira [-v ...] [-u USER] [-e URI] [-t FILE] (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER]) [-f FIELDS] [--max_results MAX_RESULTS]
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] view ISSUE
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
jira [-v ...] [-u USER] [-e URI] [-b] DUPLICATE dups ISSUE
jira [-v ...] [-u USER] [-e URI] [-b] BLOCKER blocks ISSUE
jira [-v ...] [-u USER] [-e URI] [-b] watch ISSUE [-w WATCHER]
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] comment ISSUE [-m COMMENT]
jira [-v ...] [-u USER] [-e URI] [-b] take ISSUE
jira [-v ...] [-u USER] [-e URI] [-b] (assign|give) ISSUE ASSIGNEE
jira [-v ...] [-u USER] [-e URI] [-t FILE] fields
jira [-v ...] [-u USER] [-e URI] [-t FILE] issuelinktypes
jira [-v ...] [-u USER] [-e URI] [-b][-t FILE] transmeta ISSUE
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] editmeta ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] issuetypes [-p PROJECT]
jira [-v ...] [-u USER] [-e URI] [-t FILE] createmeta [-p PROJECT] [-i ISSUETYPE]
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] transitions ISSUE
jira [-v ...] export-templates [-d DIR] [-t template]
jira [-v ...] [-u USER] [-e URI] (b|browse) ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] login
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] ISSUE
jira (ls|list) <Query Options>
jira view ISSUE
jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
jira create [--noedit] [-p PROJECT] <Create Options>
jira DUPLICATE dups ISSUE
jira BLOCKER blocks ISSUE
jira watch ISSUE [-w WATCHER]
jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
jira ack ISSUE [--edit] <Edit Options>
jira close ISSUE [--edit] <Edit Options>
jira resolve ISSUE [--edit] <Edit Options>
jira reopen ISSUE [--edit] <Edit Options>
jira start ISSUE [--edit] <Edit Options>
jira stop ISSUE [--edit] <Edit Options>
jira comment ISSUE [--noedit] <Edit Options>
jira take ISSUE
jira (assign|give) ISSUE ASSIGNEE
jira fields
jira issuelinktypes
jira transmeta ISSUE
jira editmeta ISSUE
jira issuetypes [-p PROJECT]
jira createmeta [-p PROJECT] [-i ISSUETYPE]
jira transitions ISSUE
jira export-templates [-d DIR] [-t template]
jira (b|browse) ISSUE
jira login
jira ISSUE
General Options:
-b --browse Open your browser to the Jira issue
-e --endpoint=URI URI to use for jira
-h --help Show this usage
-t --template=FILE Template file to use for output/editing
-u --user=USER Username to use for authenticaion (default: cbennett)
-u --user=USER Username to use for authentication (default: $USER)
-v --verbose Increase output logging
--version Show this version
Command Options:
Query Options:
-a --assignee=USER Username assigned the issue
-b --browse Open your browser to the Jira issue
-c --component=COMPONENT Component to Search for
-d --directory=DIR Directory to export templates to (default: /Users/cbennett/.jira.d/templates)
-f --queryfields=FIELDS Fields that are used in "list" template: (default: summary,created,priority,status,reporter,assignee)
-i --issuetype=ISSUETYPE Jira Issue Type (default: Bug)
-m --comment=COMMENT Comment message for transition
-o --override=KEY:VAL Set custom key/value pairs
-f --queryfields=FIELDS Fields that are used in "list" template: (default: summary,created,updated,priority,status,reporter,assignee)
-i --issuetype=ISSUETYPE The Issue Type
-l --limit=VAL Maximum number of results to return in query (default: 500)
-p --project=PROJECT Project to Search for
-q --query=JQL Jira Query Language expression for the search
-r --reporter=USER Reporter to search for
-w --watcher=USER Watcher to add to issue (default: cbennett)
-s --sort=ORDER For list operations, sort issues (default: priority asc, created)
-w --watcher=USER Watcher to add to issue (default: $USER)
or Watcher to search for
--max_results=VAL Maximum number of results to return in query (default: 500)
Edit Options:
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
Create Options:
-i --issuetype=ISSUETYPE Jira Issue Type (default: Bug)
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
Command Options:
-d --directory=DIR Directory to export templates to (default: $HOME/.jira.d/templates)
```
+9
View File
@@ -0,0 +1,9 @@
package jiracli
import (
"gopkg.in/Netflix-Skunkworks/go-jira.v1/lib"
)
type JiraOptions struct {
jira.SearchOptions `yaml:",inline"`
}
+31
View File
@@ -0,0 +1,31 @@
package jiracli
import (
"os"
"gopkg.in/op/go-logging.v1"
)
var (
log = logging.MustGetLogger("jiracli")
defaultFormat = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
)
func InitLogging() {
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
format := os.Getenv("JIRA_LOG_FORMAT")
if format == "" {
format = defaultFormat
}
logging.SetBackend(
logging.NewBackendFormatter(
logBackend,
logging.MustStringFormatter(format),
),
)
logging.SetLevel(logging.NOTICE, "")
}
func VerboseLogging() {
logging.SetLevel(logging.GetLevel("")+1, "")
}
+52
View File
@@ -0,0 +1,52 @@
package jiracli
import (
"os"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/lib"
"gopkg.in/alecthomas/kingpin.v2"
)
func usage() {
app := kingpin.New("jira", "Jira Command Line Client")
app.Writer(os.Stdout)
app.Version(jira.VERSION)
opts := &JiraOptions{}
// -b --browse Open your browser to the Jira issue
// -e --endpoint=URI URI to use for jira
// -k --insecure disable TLS certificate verification
// -h --help Show this usage
// -t --template=FILE Template file to use for output/editing
// -u --user=USER Username to use for authenticaion (default: %s)
// -v --verbose Increase output logging
listUsage(app, opts)
}
func listUsage(app *kingpin.Application, opts *JiraOptions) {
list := app.Command("list", "List Jira Issues")
list.Flag("assignee", "Username assigned the issue").Short('a').StringVar(&opts.Assignee)
list.Flag("component", "Component to use for query").Short('c').StringVar(&opts.Component)
list.Flag("queryfields", "Fields that are used for \"list\" template").Short('f').StringVar(&opts.QueryFields)
list.Flag("limit", "Maximum number of results to return in query").Short('l').IntVar(&opts.MaxResults)
list.Flag("project", "Project to use for query").Short('p').StringVar(&opts.Project)
list.Flag("query", "Jira Query Language expression for the search").Short('q').StringVar(&opts.Query)
list.Flag("reporter", "Reporter to use in query").Short('r').StringVar(&opts.Reporter)
list.Flag("sort", "Sort order used in query").Short('s').StringVar(&opts.Sort)
list.Flag("watcher", "Watcher to use in query").Short('w').StringVar(&opts.Watcher)
// -a --assignee=USER Username assigned the issue
// -c --component=COMPONENT Component to Search for
// -f --queryfields=FIELDS Fields that are used in "list" template: (default: %s)
// -i --issuetype=ISSUETYPE The Issue Type
// -l --limit=VAL Maximum number of results to return in query (default: %d)
// -p --project=PROJECT Project to Search for
// -q --query=JQL Jira Query Language expression for the search
// -r --reporter=USER Reporter to search for
// -s --sort=ORDER For list operations, sort issues (default: %s)
// -w --watcher=USER Watcher to add to issue (default: %s)
// or Watcher to search for
}
+19
View File
@@ -0,0 +1,19 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// AllowedValues defined from schema:
// {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// }
type AllowedValues []interface{}
+226
View File
@@ -0,0 +1,226 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// ChangeHistory defined from schema:
// {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
type ChangeHistory struct {
Author *User `json:"author,omitempty" yaml:"author,omitempty"`
Created string `json:"created,omitempty" yaml:"created,omitempty"`
HistoryMetadata *HistoryMetadata `json:"historyMetadata,omitempty" yaml:"historyMetadata,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Items Items `json:"items,omitempty" yaml:"items,omitempty"`
}
+51
View File
@@ -0,0 +1,51 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// ChangeItem defined from schema:
// {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
type ChangeItem struct {
Field string `json:"field,omitempty" yaml:"field,omitempty"`
Fieldtype string `json:"fieldtype,omitempty" yaml:"fieldtype,omitempty"`
From string `json:"from,omitempty" yaml:"from,omitempty"`
FromString string `json:"fromString,omitempty" yaml:"fromString,omitempty"`
To string `json:"to,omitempty" yaml:"to,omitempty"`
ToString string `json:"toString,omitempty" yaml:"toString,omitempty"`
}
+247
View File
@@ -0,0 +1,247 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Changelog defined from schema:
// {
// "title": "Changelog",
// "type": "object",
// "properties": {
// "histories": {
// "title": "histories",
// "type": "array",
// "items": {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// }
// }
// }
type Changelog struct {
Histories Histories `json:"histories,omitempty" yaml:"histories,omitempty"`
MaxResults int `json:"maxResults,omitempty" yaml:"maxResults,omitempty"`
StartAt int `json:"startAt,omitempty" yaml:"startAt,omitempty"`
Total int `json:"total,omitempty" yaml:"total,omitempty"`
}
+62
View File
@@ -0,0 +1,62 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// EditMeta defined from schema:
// {
// "title": "Edit Meta",
// "type": "object",
// "properties": {
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// }
// }
// }
type EditMeta struct {
Fields FieldMetaMap `json:"fields,omitempty" yaml:"fields,omitempty"`
}
+57
View File
@@ -0,0 +1,57 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// FieldMeta defined from schema:
// {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
type FieldMeta struct {
AllowedValues []interface{} `json:"allowedValues,omitempty" yaml:"allowedValues,omitempty"`
AutoCompleteURL string `json:"autoCompleteUrl,omitempty" yaml:"autoCompleteUrl,omitempty"`
HasDefaultValue bool `json:"hasDefaultValue,omitempty" yaml:"hasDefaultValue,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Operations []string `json:"operations,omitempty" yaml:"operations,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Schema interface{} `json:"schema,omitempty" yaml:"schema,omitempty"`
}
+54
View File
@@ -0,0 +1,54 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// FieldMetaMap defined from schema:
// {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// }
type FieldMetaMap map[string]*FieldMeta
+21
View File
@@ -0,0 +1,21 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchRequest.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Fields defined from schema:
// {
// "title": "fields",
// "type": "array",
// "items": {
// "type": "string"
// }
// }
type Fields []string
+224
View File
@@ -0,0 +1,224 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Histories defined from schema:
// {
// "title": "histories",
// "type": "array",
// "items": {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
// }
type Histories []*ChangeHistory
+141
View File
@@ -0,0 +1,141 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// HistoryMetadata defined from schema:
// {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
type HistoryMetadata struct {
ActivityDescription string `json:"activityDescription,omitempty" yaml:"activityDescription,omitempty"`
ActivityDescriptionKey string `json:"activityDescriptionKey,omitempty" yaml:"activityDescriptionKey,omitempty"`
Actor *HistoryMetadataParticipant `json:"actor,omitempty" yaml:"actor,omitempty"`
Cause *HistoryMetadataParticipant `json:"cause,omitempty" yaml:"cause,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
DescriptionKey string `json:"descriptionKey,omitempty" yaml:"descriptionKey,omitempty"`
EmailDescription string `json:"emailDescription,omitempty" yaml:"emailDescription,omitempty"`
EmailDescriptionKey string `json:"emailDescriptionKey,omitempty" yaml:"emailDescriptionKey,omitempty"`
ExtraData map[string]string `json:"extraData,omitempty" yaml:"extraData,omitempty"`
Generator *HistoryMetadataParticipant `json:"generator,omitempty" yaml:"generator,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
}
+45
View File
@@ -0,0 +1,45 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// HistoryMetadataParticipant defined from schema:
// {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// }
type HistoryMetadataParticipant struct {
AvatarURL string `json:"avatarUrl,omitempty" yaml:"avatarUrl,omitempty"`
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
DisplayNameKey string `json:"displayNameKey,omitempty" yaml:"displayNameKey,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
}
+18
View File
@@ -0,0 +1,18 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// IncludedFields defined from schema:
// {
// "title": "Included Fields",
// "type": "object"
// }
type IncludedFields map[string]string
+569
View File
@@ -0,0 +1,569 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Issue defined from schema:
// {
// "title": "Issue",
// "type": "object",
// "properties": {
// "changelog": {
// "title": "Changelog",
// "type": "object",
// "properties": {
// "histories": {
// "title": "histories",
// "type": "array",
// "items": {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// }
// }
// },
// "editmeta": {
// "title": "Edit Meta",
// "type": "object",
// "properties": {
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// }
// }
// },
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "fieldsToInclude": {
// "title": "Included Fields",
// "type": "object"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "names": {
// "title": "names",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "operations": {
// "title": "Opsbar",
// "type": "object",
// "properties": {
// "linkGroups": {
// "title": "linkGroups",
// "type": "array",
// "items": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// }
// }
// },
// "properties": {
// "title": "Properties",
// "type": "object",
// "properties": {
// "properties": {
// "title": "properties",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// }
// }
// },
// "renderedFields": {
// "title": "renderedFields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "schema": {
// "title": "schema",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
// }
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "transitions": {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// },
// "versionedRepresentations": {
// "title": "versionedRepresentations",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// }
// }
// }
// }
// }
type Issue struct {
Changelog *Changelog `json:"changelog,omitempty" yaml:"changelog,omitempty"`
Editmeta *EditMeta `json:"editmeta,omitempty" yaml:"editmeta,omitempty"`
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Fields map[string]interface{} `json:"fields,omitempty" yaml:"fields,omitempty"`
FieldsToInclude IncludedFields `json:"fieldsToInclude,omitempty" yaml:"fieldsToInclude,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Names map[string]string `json:"names,omitempty" yaml:"names,omitempty"`
Operations *Opsbar `json:"operations,omitempty" yaml:"operations,omitempty"`
Properties *Properties `json:"properties,omitempty" yaml:"properties,omitempty"`
RenderedFields map[string]interface{} `json:"renderedFields,omitempty" yaml:"renderedFields,omitempty"`
Schema JSONTypeMap `json:"schema,omitempty" yaml:"schema,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
Transitions Transitions `json:"transitions,omitempty" yaml:"transitions,omitempty"`
VersionedRepresentations map[string]map[string]interface{} `json:"versionedRepresentations,omitempty" yaml:"versionedRepresentations,omitempty"`
}
+557
View File
@@ -0,0 +1,557 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Issues defined from schema:
// {
// "title": "issues",
// "type": "array",
// "items": {
// "title": "Issue",
// "type": "object",
// "properties": {
// "changelog": {
// "title": "Changelog",
// "type": "object",
// "properties": {
// "histories": {
// "title": "histories",
// "type": "array",
// "items": {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// }
// }
// },
// "editmeta": {
// "title": "Edit Meta",
// "type": "object",
// "properties": {
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// }
// }
// },
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "fieldsToInclude": {
// "title": "Included Fields",
// "type": "object"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "names": {
// "title": "names",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "operations": {
// "title": "Opsbar",
// "type": "object",
// "properties": {
// "linkGroups": {
// "title": "linkGroups",
// "type": "array",
// "items": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// }
// }
// },
// "properties": {
// "title": "Properties",
// "type": "object",
// "properties": {
// "properties": {
// "title": "properties",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// }
// }
// },
// "renderedFields": {
// "title": "renderedFields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "schema": {
// "title": "schema",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
// }
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "transitions": {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// },
// "versionedRepresentations": {
// "title": "versionedRepresentations",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// }
// }
// }
// }
// }
// }
type Issues []*Issue
+48
View File
@@ -0,0 +1,48 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Items defined from schema:
// {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
type Items []*ChangeItem
+41
View File
@@ -0,0 +1,41 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// JSONTypeMap defined from schema:
// {
// "title": "schema",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
// }
// }
type JSONTypeMap map[string]*JSONType
+41
View File
@@ -0,0 +1,41 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// JSONType defined from schema:
// {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
type JSONType struct {
Custom string `json:"custom,omitempty" yaml:"custom,omitempty"`
CustomID int `json:"customId,omitempty" yaml:"customId,omitempty"`
Items string `json:"items,omitempty" yaml:"items,omitempty"`
System string `json:"system,omitempty" yaml:"system,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
}
+51
View File
@@ -0,0 +1,51 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// LinkGroup defined from schema:
// {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
type LinkGroup struct {
Groups []interface{} `json:"groups,omitempty" yaml:"groups,omitempty"`
Header interface{} `json:"header,omitempty" yaml:"header,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Links []interface{} `json:"links,omitempty" yaml:"links,omitempty"`
StyleClass string `json:"styleClass,omitempty" yaml:"styleClass,omitempty"`
Weight int `json:"weight,omitempty" yaml:"weight,omitempty"`
}
+48
View File
@@ -0,0 +1,48 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// LinkGroups defined from schema:
// {
// "title": "linkGroups",
// "type": "array",
// "items": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// }
type LinkGroups []*LinkGroup
+21
View File
@@ -0,0 +1,21 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Operations defined from schema:
// {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// }
type Operations []string
+56
View File
@@ -0,0 +1,56 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Opsbar defined from schema:
// {
// "title": "Opsbar",
// "type": "object",
// "properties": {
// "linkGroups": {
// "title": "linkGroups",
// "type": "array",
// "items": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// }
// }
// }
type Opsbar struct {
LinkGroups LinkGroups `json:"linkGroups,omitempty" yaml:"linkGroups,omitempty"`
}
+31
View File
@@ -0,0 +1,31 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Properties defined from schema:
// {
// "title": "Properties",
// "type": "object",
// "properties": {
// "properties": {
// "title": "properties",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// }
// }
// }
type Properties struct {
Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`
}
+55
View File
@@ -0,0 +1,55 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchRequest.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// SearchRequest defined from schema:
// {
// "title": "Search Request",
// "id": "https://docs.atlassian.com/jira/REST/schema/search-request#",
// "type": "object",
// "properties": {
// "fields": {
// "title": "fields",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "fieldsByKeys": {
// "title": "fieldsByKeys",
// "type": "boolean"
// },
// "jql": {
// "title": "jql",
// "type": "string"
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "validateQuery": {
// "title": "validateQuery",
// "type": "boolean"
// }
// }
// }
type SearchRequest struct {
Fields Fields `json:"fields,omitempty" yaml:"fields,omitempty"`
FieldsByKeys bool `json:"fieldsByKeys,omitempty" yaml:"fieldsByKeys,omitempty"`
JQL string `json:"jql,omitempty" yaml:"jql,omitempty"`
MaxResults int `json:"maxResults,omitempty" yaml:"maxResults,omitempty"`
StartAt int `json:"startAt,omitempty" yaml:"startAt,omitempty"`
ValidateQuery bool `json:"validateQuery,omitempty" yaml:"validateQuery,omitempty"`
}
+770
View File
@@ -0,0 +1,770 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// SearchResults defined from schema:
// {
// "title": "Search Results",
// "id": "https://docs.atlassian.com/jira/REST/schema/search-results#",
// "type": "object",
// "definitions": {
// "field-meta": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// },
// "history-metadata-participant": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "json-type": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// },
// "link-group": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// },
// "simple-link": {
// "title": "Simple Link",
// "type": "object",
// "properties": {
// "href": {
// "type": "string"
// },
// "iconClass": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "label": {
// "type": "string"
// },
// "styleClass": {
// "type": "string"
// },
// "title": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// },
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "issues": {
// "title": "issues",
// "type": "array",
// "items": {
// "title": "Issue",
// "type": "object",
// "properties": {
// "changelog": {
// "title": "Changelog",
// "type": "object",
// "properties": {
// "histories": {
// "title": "histories",
// "type": "array",
// "items": {
// "title": "Change History",
// "type": "object",
// "properties": {
// "author": {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// },
// "created": {
// "title": "created",
// "type": "string"
// },
// "historyMetadata": {
// "title": "History Metadata",
// "type": "object",
// "properties": {
// "activityDescription": {
// "title": "activityDescription",
// "type": "string"
// },
// "activityDescriptionKey": {
// "title": "activityDescriptionKey",
// "type": "string"
// },
// "actor": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "cause": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "description": {
// "title": "description",
// "type": "string"
// },
// "descriptionKey": {
// "title": "descriptionKey",
// "type": "string"
// },
// "emailDescription": {
// "title": "emailDescription",
// "type": "string"
// },
// "emailDescriptionKey": {
// "title": "emailDescriptionKey",
// "type": "string"
// },
// "extraData": {
// "title": "extraData",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "generator": {
// "title": "History Metadata Participant",
// "type": "object",
// "properties": {
// "avatarUrl": {
// "type": "string"
// },
// "displayName": {
// "type": "string"
// },
// "displayNameKey": {
// "type": "string"
// },
// "id": {
// "type": "string"
// },
// "type": {
// "type": "string"
// },
// "url": {
// "type": "string"
// }
// }
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "items": {
// "title": "items",
// "type": "array",
// "items": {
// "title": "Change Item",
// "type": "object",
// "properties": {
// "field": {
// "title": "field",
// "type": "string"
// },
// "fieldtype": {
// "title": "fieldtype",
// "type": "string"
// },
// "from": {
// "title": "from",
// "type": "string"
// },
// "fromString": {
// "title": "fromString",
// "type": "string"
// },
// "to": {
// "title": "to",
// "type": "string"
// },
// "toString": {
// "title": "toString",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// }
// }
// },
// "editmeta": {
// "title": "Edit Meta",
// "type": "object",
// "properties": {
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// }
// }
// },
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "fieldsToInclude": {
// "title": "Included Fields",
// "type": "object"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "names": {
// "title": "names",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "operations": {
// "title": "Opsbar",
// "type": "object",
// "properties": {
// "linkGroups": {
// "title": "linkGroups",
// "type": "array",
// "items": {
// "title": "Link Group",
// "type": "object",
// "properties": {
// "groups": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/link-group"
// }
// },
// "header": {
// "$ref": "#/definitions/simple-link"
// },
// "id": {
// "type": "string"
// },
// "links": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/simple-link"
// }
// },
// "styleClass": {
// "type": "string"
// },
// "weight": {
// "type": "integer"
// }
// }
// }
// }
// }
// },
// "properties": {
// "title": "Properties",
// "type": "object",
// "properties": {
// "properties": {
// "title": "properties",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// }
// }
// },
// "renderedFields": {
// "title": "renderedFields",
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// },
// "schema": {
// "title": "schema",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
// }
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "transitions": {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// },
// "versionedRepresentations": {
// "title": "versionedRepresentations",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "object",
// "patternProperties": {
// ".+": {}
// }
// }
// }
// }
// }
// }
// },
// "maxResults": {
// "title": "maxResults",
// "type": "integer"
// },
// "names": {
// "title": "names",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "schema": {
// "title": "schema",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "type": "string"
// },
// "customId": {
// "type": "integer"
// },
// "items": {
// "type": "string"
// },
// "system": {
// "type": "string"
// },
// "type": {
// "type": "string"
// }
// }
// }
// }
// },
// "startAt": {
// "title": "startAt",
// "type": "integer"
// },
// "total": {
// "title": "total",
// "type": "integer"
// },
// "warningMessages": {
// "title": "warningMessages",
// "type": "array",
// "items": {
// "type": "string"
// }
// }
// }
// }
type SearchResults struct {
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Issues Issues `json:"issues,omitempty" yaml:"issues,omitempty"`
MaxResults int `json:"maxResults,omitempty" yaml:"maxResults,omitempty"`
Names map[string]string `json:"names,omitempty" yaml:"names,omitempty"`
Schema JSONTypeMap `json:"schema,omitempty" yaml:"schema,omitempty"`
StartAt int `json:"startAt,omitempty" yaml:"startAt,omitempty"`
Total int `json:"total,omitempty" yaml:"total,omitempty"`
WarningMessages WarningMessages `json:"warningMessages,omitempty" yaml:"warningMessages,omitempty"`
}
+78
View File
@@ -0,0 +1,78 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Status defined from schema:
// {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
type Status struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
IconURL string `json:"iconUrl,omitempty" yaml:"iconUrl,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
StatusCategory *StatusCategory `json:"statusCategory,omitempty" yaml:"statusCategory,omitempty"`
StatusColor string `json:"statusColor,omitempty" yaml:"statusColor,omitempty"`
}
+46
View File
@@ -0,0 +1,46 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// StatusCategory defined from schema:
// {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// }
type StatusCategory struct {
ColorName string `json:"colorName,omitempty" yaml:"colorName,omitempty"`
ID int `json:"id,omitempty" yaml:"id,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
}
+139
View File
@@ -0,0 +1,139 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Transition defined from schema:
// {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
type Transition struct {
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Fields FieldMetaMap `json:"fields,omitempty" yaml:"fields,omitempty"`
HasScreen bool `json:"hasScreen,omitempty" yaml:"hasScreen,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
To *Status `json:"to,omitempty" yaml:"to,omitempty"`
}
+136
View File
@@ -0,0 +1,136 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Transitions defined from schema:
// {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "type": "string"
// },
// "hasDefaultValue": {
// "type": "boolean"
// },
// "key": {
// "type": "string"
// },
// "name": {
// "type": "string"
// },
// "operations": {
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "type": "boolean"
// },
// "schema": {
// "$ref": "#/definitions/json-type"
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// }
type Transitions []*Transition
+18
View File
@@ -0,0 +1,18 @@
package jiradata
import (
"strings"
)
// Find will search the transitions for one that matches
// the given name. It will return a valid trantion that matches
// or nil
func (t Transitions) Find(name string) *Transition {
name = strings.ToLower(name)
for _, trans := range t {
if strings.Contains(strings.ToLower(trans.Name), name) {
return trans
}
}
return nil
}
+180
View File
@@ -0,0 +1,180 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// TransitionsMeta defined from schema:
// {
// "title": "Transitions Meta",
// "id": "https://docs.atlassian.com/jira/REST/schema/transitions-meta#",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "transitions": {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// }
// }
// }
type TransitionsMeta struct {
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Transitions Transitions `json:"transitions,omitempty" yaml:"transitions,omitempty"`
}
+66
View File
@@ -0,0 +1,66 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// User defined from schema:
// {
// "title": "User",
// "type": "object",
// "properties": {
// "active": {
// "title": "active",
// "type": "boolean"
// },
// "avatarUrls": {
// "title": "avatarUrls",
// "type": "object",
// "patternProperties": {
// ".+": {
// "type": "string"
// }
// }
// },
// "displayName": {
// "title": "displayName",
// "type": "string"
// },
// "emailAddress": {
// "title": "emailAddress",
// "type": "string"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "timeZone": {
// "title": "timeZone",
// "type": "string"
// }
// }
// }
type User struct {
Active bool `json:"active,omitempty" yaml:"active,omitempty"`
AvatarUrls map[string]string `json:"avatarUrls,omitempty" yaml:"avatarUrls,omitempty"`
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
EmailAddress string `json:"emailAddress,omitempty" yaml:"emailAddress,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
TimeZone string `json:"timeZone,omitempty" yaml:"timeZone,omitempty"`
}
+21
View File
@@ -0,0 +1,21 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/SearchResults.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// WarningMessages defined from schema:
// {
// "title": "warningMessages",
// "type": "array",
// "items": {
// "type": "string"
// }
// }
type WarningMessages []string
Generated
+71
View File
@@ -0,0 +1,71 @@
hash: c912ef7d849008c13494df9deb6ae3f55b7b7472704ea15253bea32d2f8a219a
updated: 2016-09-25T12:49:13.264637268-07:00
imports:
- name: github.com/alecthomas/template
version: a0175ee3bccc567396460bf5acd36800cb10c49c
subpackages:
- parse
- name: github.com/alecthomas/units
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
- name: github.com/coryb/optigo
version: 1b53172e5b4a00b528ea9fde88a4b76b596e293e
- name: github.com/howeyc/gopass
version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
- name: github.com/kballard/go-shellquote
version: d8ec1a69a250a17bb0e419c386eac1f3711dc142
- name: github.com/mattn/go-colorable
version: ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8
- name: github.com/mattn/go-isatty
version: 3a115632dcd687f9c8cd01679c83a06a0e21c1f3
- name: github.com/mgutz/ansi
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
- name: github.com/op/go-logging
version: 970db520ece77730c7e4724c61121037378659d9
- name: golang.org/x/crypto
version: b13fc1fd382d01861b16b2e6474487d3d4d27f20
subpackages:
- acme
- blowfish
- cast5
- curve25519
- ed25519
- ed25519/internal/edwards25519
- hkdf
- nacl/secretbox
- openpgp
- openpgp/armor
- openpgp/elgamal
- openpgp/errors
- openpgp/packet
- openpgp/s2k
- pbkdf2
- pkcs12/internal/rc2
- poly1305
- ripemd160
- salsa20/salsa
- ssh
- ssh/agent
- ssh/terminal
- ssh/testdata
- name: golang.org/x/net
version: 6250b412798208e6c90b03b7c4f226de5aa299e2
subpackages:
- context
- context/ctxhttp
- name: golang.org/x/sys
version: a646d33e2ee3172a661fc09bca23bb4889a41bc8
subpackages:
- unix
- name: golang.org/x/tools
version: c2ef61f450233d3c629ade8eb2083e43fa75c97a
subpackages:
- cmd/gorename
- name: gopkg.in/alecthomas/kingpin.v2
version: e9044be3ab2a8e11d4e1f418d12f0790d57e8d70
- name: gopkg.in/coryb/yaml.v2
version: bd17d4d1209d8af092496a57db259d2d472676eb
- name: gopkg.in/op/go-logging.v1
version: b2cb9fa56473e98db8caba80237377e83fe44db5
testImports:
- name: gopkg.in/check.v1
version: 4f90aeace3a26ad7021961c297b22c42160c7b25
+15
View File
@@ -0,0 +1,15 @@
package: .
import:
- package: github.com/coryb/optigo
version: ^0.0.4
- package: github.com/howeyc/gopass
- package: github.com/kballard/go-shellquote
- package: github.com/mgutz/ansi
- package: gopkg.in/coryb/yaml.v2
- package: gopkg.in/op/go-logging.v1
version: ^1.0.0
- package: gopkg.in/alecthomas/kingpin.v2
version: ^2.2.3
- package: golang.org/x/tools
subpackages:
- cmd/gorename
-437
View File
@@ -1,437 +0,0 @@
package cli
import (
"bytes"
"encoding/json"
"fmt"
"github.com/kballard/go-shellquote"
"github.com/op/go-logging"
"gopkg.in/coryb/yaml.v2"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"os/exec"
"runtime"
"strings"
"time"
)
var log = logging.MustGetLogger("jira.cli")
type Cli struct {
endpoint *url.URL
opts map[string]interface{}
cookieFile string
ua *http.Client
}
func New(opts map[string]interface{}) *Cli {
homedir := os.Getenv("HOME")
cookieJar, _ := cookiejar.New(nil)
endpoint, _ := opts["endpoint"].(string)
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
if project, ok := opts["project"].(string); ok {
opts["project"] = strings.ToUpper(project)
}
cli := &Cli{
endpoint: url,
opts: opts,
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
ua: &http.Client{Jar: cookieJar},
}
cli.ua.Jar.SetCookies(url, cli.loadCookies())
return cli
}
func (c *Cli) saveCookies(cookies []*http.Cookie) {
// expiry in one week from now
expiry := time.Now().Add(24 * 7 * time.Hour)
for _, cookie := range cookies {
cookie.Expires = expiry
}
if currentCookies := c.loadCookies(); currentCookies != nil {
currentCookiesByName := make(map[string]*http.Cookie)
for _, cookie := range currentCookies {
currentCookiesByName[cookie.Name] = cookie
}
for _, cookie := range cookies {
currentCookiesByName[cookie.Name] = cookie
}
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
for _, v := range currentCookiesByName {
mergedCookies = append(mergedCookies, v)
}
jsonWrite(c.cookieFile, mergedCookies)
} else {
jsonWrite(c.cookieFile, cookies)
}
}
func (c *Cli) loadCookies() []*http.Cookie {
bytes, err := ioutil.ReadFile(c.cookieFile)
if err != nil && os.IsNotExist(err) {
// dont load cookies if the file does not exist
return nil
}
if err != nil {
log.Error("Failed to open %s: %s", c.cookieFile, err)
os.Exit(1)
}
cookies := make([]*http.Cookie, 0)
err = json.Unmarshal(bytes, &cookies)
if err != nil {
log.Error("Failed to parse json from file %s: %s", c.cookieFile, err)
}
log.Debug("Loading Cookies: %s", cookies)
return cookies
}
func (c *Cli) post(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("POST", uri, content)
}
func (c *Cli) put(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("PUT", uri, content)
}
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) {
buffer := bytes.NewBufferString(content)
req, _ := http.NewRequest(method, uri, buffer)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte, 0, len(content)))
req.Write(logBuffer)
log.Debug("%s", logBuffer)
// need to recreate the buffer since the offset is now at the end
// need to be able to rewind the buffer offset, dont know how yet
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
}
if resp, err := c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
return c.makeRequest(req)
}
return resp, err
}
}
func (c *Cli) get(uri string) (*http.Response, error) {
req, _ := http.NewRequest("GET", uri, nil)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte, 0))
req.Write(logBuffer)
log.Debug("%s", logBuffer)
}
if resp, err := c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
return c.makeRequest(req)
}
return resp, err
}
}
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("Content-Type", "application/json")
if resp, err = c.ua.Do(req); err != nil {
log.Error("Failed to %s %s: %s", req.Method, req.URL.String(), err)
return nil, err
} else {
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
log.Error("response status: %s", resp.Status)
}
runtime.SetFinalizer(resp, func(r *http.Response) {
r.Body.Close()
})
if _, ok := resp.Header["Set-Cookie"]; ok {
c.saveCookies(resp.Cookies())
}
}
return resp, nil
}
func (c *Cli) getTemplate(name string) string {
if override, ok := c.opts["template"].(string); ok {
if _, err := os.Stat(override); err == nil {
return readFile(override)
} else {
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", override)); err == nil {
return readFile(file)
}
if dflt, ok := all_templates[override]; ok {
return dflt
}
}
}
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", name)); err != nil {
// create-bug etc are special, if we dont find it in the path
// then just return a generic create template
if strings.HasPrefix(name, "create-") {
if file, err := FindClosestParentPath(".jira.d/templates/create"); err != nil {
return all_templates["create"]
} else {
return readFile(file)
}
}
return all_templates[name]
} else {
return readFile(file)
}
}
type NoChangesFound struct{}
func (f NoChangesFound) Error() string {
return "No changes found, aborting"
}
func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData map[string]interface{}, templateProcessor func(string) error) error {
tmpdir := fmt.Sprintf("%s/.jira.d/tmp", os.Getenv("HOME"))
if err := mkdir(tmpdir); err != nil {
return err
}
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix)
if err != nil {
log.Error("Failed to make temp file in %s: %s", tmpdir, err)
return err
}
defer fh.Close()
tmpFileName := fmt.Sprintf("%s.yml", fh.Name())
if err := os.Rename(fh.Name(), tmpFileName); err != nil {
log.Error("Failed to rename %s to %s: %s", fh.Name(), fmt.Sprintf("%s.yml", fh.Name()), err)
return err
}
defer func() {
os.Remove(tmpFileName)
}()
err = runTemplate(template, templateData, fh)
if err != nil {
return err
}
fh.Close()
editor, ok := c.opts["editor"].(string)
if !ok {
editor = os.Getenv("JIRA_EDITOR")
if editor == "" {
editor = os.Getenv("EDITOR")
if editor == "" {
editor = "vim"
}
}
}
editing := c.getOptBool("edit", true)
tmpFileNameOrig := fmt.Sprintf("%s.orig", tmpFileName)
copyFile(tmpFileName, tmpFileNameOrig)
defer func() {
os.Remove(tmpFileNameOrig)
}()
for true {
if editing {
shell, _ := shellquote.Split(editor)
shell = append(shell, tmpFileName)
log.Debug("Running: %#v", shell)
cmd := exec.Command(shell[0], shell[1:]...)
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
if err := cmd.Run(); err != nil {
log.Error("Failed to edit template with %s: %s", editor, err)
if promptYN("edit again?", true) {
continue
}
return err
}
diff := exec.Command("diff", "-q", tmpFileNameOrig, tmpFileName)
// if err == nil then diff found no changes
if err := diff.Run(); err == nil {
return NoChangesFound{}
}
}
edited := make(map[string]interface{})
if fh, err := ioutil.ReadFile(tmpFileName); err != nil {
log.Error("Failed to read tmpfile %s: %s", tmpFileName, err)
if editing && promptYN("edit again?", true) {
continue
}
return err
} else {
if err := yaml.Unmarshal(fh, &edited); err != nil {
log.Error("Failed to parse YAML: %s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
}
if fixed, err := yamlFixup(edited); err != nil {
return err
} else {
edited = fixed.(map[string]interface{})
}
// if you want to abort editing a jira issue then
// you can add the "abort: true" flag to the document
// and we will abort now
if val, ok := edited["abort"].(bool); ok && val {
log.Info("abort flag found in template, quiting")
return fmt.Errorf("abort flag found in template, quiting")
}
if _, ok := templateData["meta"]; ok {
mf := templateData["meta"].(map[string]interface{})["fields"]
if f, ok := edited["fields"].(map[string]interface{}); ok {
for k := range f {
if _, ok := mf.(map[string]interface{})[k]; !ok {
err := fmt.Errorf("Field %s is not editable", k)
log.Error("%s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
}
}
}
json, err := jsonEncode(edited)
if err != nil {
return err
}
if err := templateProcessor(json); err != nil {
log.Error("%s", err)
if editing && promptYN("edit again?", true) {
continue
}
}
return nil
}
return nil
}
func (c *Cli) Browse(issue string) error {
if val, ok := c.opts["browse"].(bool); ok && val {
if runtime.GOOS == "darwin" {
return exec.Command("open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
} else if runtime.GOOS == "linux" {
return exec.Command("xdg-open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
}
}
return nil
}
func (c *Cli) FindIssues() (interface{}, error) {
var query string
var ok bool
// project = BAKERY and status not in (Resolved, Closed)
if query, ok = c.opts["query"].(string); !ok {
qbuff := bytes.NewBufferString("resolution = unresolved")
if project, ok := c.opts["project"]; !ok {
err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required")
log.Error("%s", err)
return nil, err
} else {
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
}
if component, ok := c.opts["component"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
}
if assignee, ok := c.opts["assignee"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND assignee = '%s'", assignee))
}
if issuetype, ok := c.opts["issuetype"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND issuetype = '%s'", issuetype))
}
if watcher, ok := c.opts["watcher"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND watcher = '%s'", watcher))
}
if reporter, ok := c.opts["reporter"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND reporter = '%s'", reporter))
}
if sort, ok := c.opts["sort"]; ok && sort != "" {
qbuff.WriteString(fmt.Sprintf(" ORDER BY %s", sort))
}
query = qbuff.String()
}
fields := make([]string, 0)
if qf, ok := c.opts["queryfields"].(string); ok {
fields = strings.Split(qf, ",")
} else {
fields = append(fields, "summary")
}
json, err := jsonEncode(map[string]interface{}{
"jql": query,
"startAt": "0",
"maxResults": c.opts["max_results"],
"fields": fields,
})
if err != nil {
return nil, err
}
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
if data, err := responseToJson(c.post(uri, json)); err != nil {
return nil, err
} else {
return data, nil
}
}
func (c *Cli) getOptString(optName string, dflt string) string {
if val, ok := c.opts[optName].(string); ok {
return val
} else {
return dflt
}
}
func (c *Cli) getOptBool(optName string, dflt bool) bool {
if val, ok := c.opts[optName].(bool); ok {
return val
} else {
return dflt
}
}
-574
View File
@@ -1,574 +0,0 @@
package cli
import (
"bytes"
"code.google.com/p/gopass"
"fmt"
"net/http"
"os"
"strings"
// "github.com/kr/pretty"
)
func (c *Cli) CmdLogin() error {
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
for true {
req, _ := http.NewRequest("GET", uri, nil)
user, _ := c.opts["user"].(string)
prompt := fmt.Sprintf("Enter Password for %s: ", user)
passwd, _ := gopass.GetPass(prompt)
req.SetBasicAuth(user, passwd)
if resp, err := c.makeRequest(req); err != nil {
return err
} else {
if resp.StatusCode == 403 {
// probably got this, need to redirect the user to login manually
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
err := fmt.Errorf("Authenticaion Failed: %s", reason)
log.Error("%s", err)
return err
}
err := fmt.Errorf("Authentication Failed: Unknown Reason")
log.Error("%s", err)
return err
} else if resp.StatusCode == 200 {
// https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs
// probably bad password, try again
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
log.Warning("Authentication Failed: %s", reason)
continue
}
} else {
log.Warning("Login failed")
continue
}
}
return nil
}
return nil
}
func (c *Cli) CmdFields() error {
log.Debug("fields called")
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("fields"), data, nil)
}
func (c *Cli) CmdList() error {
log.Debug("list called")
if data, err := c.FindIssues(); err != nil {
return err
} else {
return runTemplate(c.getTemplate("list"), data, nil)
}
}
func (c *Cli) CmdView(issue string) error {
log.Debug("view called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("view"), data, nil)
}
func (c *Cli) CmdEdit(issue string) error {
log.Debug("edit called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
editmeta, err := responseToJson(c.get(uri))
if err != nil {
return err
}
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
var issueData map[string]interface{}
if data, err := responseToJson(c.get(uri)); err != nil {
return err
} else {
issueData = data.(map[string]interface{})
}
issueData["meta"] = editmeta.(map[string]interface{})
issueData["overrides"] = c.opts
return c.editTemplate(
c.getTemplate("edit"),
fmt.Sprintf("%s-edit-", issue),
issueData,
func(json string) error {
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping PUT")
return nil
}
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
c.Browse(issueData["key"].(string))
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Error("%s:\n%s", err, logBuffer)
return err
}
},
)
}
func (c *Cli) CmdEditMeta(issue string) error {
log.Debug("editMeta called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("editmeta"), data, nil)
}
func (c *Cli) CmdTransitionMeta(issue string) error {
log.Debug("tranisionMeta called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("transmeta"), data, nil)
}
func (c *Cli) CmdIssueTypes() error {
project := c.opts["project"].(string)
log.Debug("issueTypes called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("issuetypes"), data, nil)
}
func (c *Cli) CmdCreateMeta() error {
project := c.opts["project"].(string)
issuetype := c.getOptString("issuetype", "Bug")
log.Debug("createMeta called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
if val, ok := data.(map[string]interface{})["projects"]; ok {
if len(val.([]interface{})) == 0 {
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to createmeta.", project, issuetype)
log.Error("%s", err)
return err
}
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
data = val.([]interface{})[0]
}
}
return runTemplate(c.getTemplate("createmeta"), data, nil)
}
func (c *Cli) CmdTransitions(issue string) error {
log.Debug("Transitions called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("transitions"), data, nil)
}
func (c *Cli) CmdCreate() error {
project := c.opts["project"].(string)
issuetype := c.getOptString("issuetype", "Bug")
log.Debug("create called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
issueData := make(map[string]interface{})
issueData["overrides"] = c.opts
issueData["overrides"].(map[string]string)["issuetype"] = issuetype
if val, ok := data.(map[string]interface{})["projects"]; ok {
if len(val.([]interface{})) == 0 {
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to create issue.", project, issuetype)
log.Error("%s", err)
return err
}
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
issueData["meta"] = val.([]interface{})[0]
}
}
sanitizedType := strings.ToLower(strings.Replace(issuetype, " ", "", -1))
return c.editTemplate(
c.getTemplate(fmt.Sprintf("create-%s", sanitizedType)),
fmt.Sprintf("create-%s-", sanitizedType),
issueData,
func(json string) error {
log.Debug("JSON: %s", json)
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
// response: {"id":"410836","key":"PROJ-238","self":"https://jira/rest/api/2/issue/410836"}
if json, err := responseToJson(resp, nil); err != nil {
return err
} else {
key := json.(map[string]interface{})["key"]
c.Browse(key.(string))
fmt.Printf("OK %s %s/browse/%s\n", key, c.endpoint, key)
}
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
},
)
return nil
}
func (c *Cli) CmdIssueLinkTypes() error {
log.Debug("Transitions called")
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("issuelinktypes"), data, nil)
}
func (c *Cli) CmdBlocks(blocker string, issue string) error {
log.Debug("blocks called")
json, err := jsonEncode(map[string]interface{}{
"type": map[string]string{
"name": "Depends", // TODO This is probably not constant across Jira installs
},
"inwardIssue": map[string]string{
"key": issue,
},
"outwardIssue": map[string]string{
"key": blocker,
},
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
return nil
}
func (c *Cli) CmdDups(duplicate string, issue string) error {
log.Debug("dups called")
json, err := jsonEncode(map[string]interface{}{
"type": map[string]string{
"name": "Duplicate", // TODO This is probably not constant across Jira installs
},
"inwardIssue": map[string]string{
"key": duplicate,
},
"outwardIssue": map[string]string{
"key": issue,
},
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
return nil
}
func (c *Cli) CmdWatch(issue string) error {
watcher := c.getOptString("watcher", c.opts["user"].(string))
log.Debug("watch called")
json, err := jsonEncode(watcher)
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
return nil
}
func (c *Cli) CmdTransition(issue string, trans string) error {
log.Debug("transition called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
transitions := data.(map[string]interface{})["transitions"].([]interface{})
var transId, transName string
var transMeta map[string]interface{}
found := make([]string, 0, len(transitions))
for _, transition := range transitions {
name := transition.(map[string]interface{})["name"].(string)
id := transition.(map[string]interface{})["id"].(string)
found = append(found, name)
if strings.Contains(strings.ToLower(name), trans) {
transName = name
transId = id
transMeta = transition.(map[string]interface{})
}
}
if transId == "" {
err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", "))
log.Error("%s", err)
return err
}
handlePost := func(json string) error {
log.Debug("POST: %s", json)
// os.Exit(0)
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
return nil
}
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
var issueData map[string]interface{}
if data, err := responseToJson(c.get(uri)); err != nil {
return err
} else {
issueData = data.(map[string]interface{})
}
issueData["meta"] = transMeta
issueData["overrides"] = c.opts
issueData["transition"] = map[string]interface{}{
"name": transName,
"id": transId,
}
return c.editTemplate(
c.getTemplate("transition"),
fmt.Sprintf("%s-trans-%s-", issue, trans),
issueData,
handlePost,
)
}
func (c *Cli) CmdComment(issue string) error {
log.Debug("comment called")
handlePost := func(json string) error {
log.Debug("JSON: %s", json)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
return err
}
}
if comment, ok := c.opts["comment"]; ok && comment != "" {
json, err := jsonEncode(map[string]interface{}{
"body": comment,
})
if err != nil {
return err
}
return handlePost(json)
} else {
return c.editTemplate(
c.getTemplate("comment"),
fmt.Sprintf("%s-create-", issue),
map[string]interface{}{},
handlePost,
)
}
return nil
}
func (c *Cli) CmdAssign(issue string, user string) error {
log.Debug("assign called")
json, err := jsonEncode(map[string]interface{}{
"name": user,
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
if c.getOptBool("dryrun", false) {
log.Debug("Dryrun mode, skipping PUT")
return nil
}
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
c.Browse(issue)
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Error("%s:\n%s", err, logBuffer)
return err
}
return nil
}
func (c *Cli) CmdExportTemplates() error {
dir := c.opts["directory"].(string)
if err := mkdir(dir); err != nil {
return err
}
for name, template := range all_templates {
if wanted, ok := c.opts["template"]; ok && wanted != name {
continue
}
templateFile := fmt.Sprintf("%s/%s", dir, name)
if _, err := os.Stat(templateFile); err == nil {
log.Warning("Skipping %s, already exists", templateFile)
continue
}
if fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
log.Error("Failed to open %s for writing: %s", templateFile, err)
return err
} else {
defer fh.Close()
log.Notice("Creating %s", templateFile)
fh.Write([]byte(template))
}
}
return nil
}
-146
View File
@@ -1,146 +0,0 @@
package cli
var all_templates = map[string]string{
"debug": default_debug_template,
"fields": default_debug_template,
"editmeta": default_debug_template,
"transmeta": default_debug_template,
"createmeta": default_debug_template,
"issuelinktypes": default_debug_template,
"list": default_list_template,
"table": default_table_template,
"view": default_view_template,
"edit": default_edit_template,
"transitions": default_transitions_template,
"issuetypes": default_issuetypes_template,
"create": default_create_template,
"comment": default_comment_template,
"transition": default_transition_template,
}
const default_debug_template = "{{ . | toJson}}\n"
const default_list_template = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
const default_table_template = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
| {{ "Issue" | printf "%-14s" }} | {{ "Summary" | printf "%-55s" }} | {{ "Priority" | printf "%-12s" }} | {{ "Status" | printf "%-12s" }} | {{ "Age" | printf "%-10s" }} | {{ "Reporter" | printf "%-12s" }} | {{ "Assignee" | printf "%-12s" }} |
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
{{ range .issues }}| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned>{{end}} |
{{ end }}+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
`
const default_view_template = `issue: {{ .key }}
created: {{ .fields.created }}
status: {{ .fields.status.name }}
summary: {{ .fields.summary }}
project: {{ .fields.project.key }}
components: {{ range .fields.components }}{{ .name }} {{end}}
issuetype: {{ .fields.issuetype.name }}
assignee: {{ if .fields.assignee }}{{ .fields.assignee.name }}{{end}}
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}[{{.outwardIssue.fields.status.name}}]{{end}}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}
priority: {{ .fields.priority.name }}
description: |
{{ or .fields.description "" | indent 2 }}
comments:
{{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
{{ or .body "" | indent 4}}
{{end}}
`
const default_edit_template = `# issue: {{ .key }}
update:
comment:
- add:
body: |~
{{ or .overrides.comment "" | indent 10 }}
fields:
summary: {{ or .overrides.summary .fields.summary }}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
- name: {{ .name }}{{end}}{{end}}
assignee:
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
reporter:
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
# watchers
customfield_10110: {{ range .fields.customfield_10110 }}
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
- name: {{ .overrides.watcher}}{{end}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority .fields.priority.name }}
description: |~
{{ or .overrides.description (or .fields.description "") | indent 4 }}
# comments:
# {{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
# {{ or .body "" | indent 4 | comment}}
# {{end}}
`
const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}}
{{end}}`
const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
{{end}}{{end}}`
const default_create_template = `fields:
project:
key: {{ .overrides.project }}
issuetype:
name: {{ .overrides.issuetype }}
summary: {{ or .overrides.summary "" }}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority "unassigned" }}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
- name: {{ . }}{{end}}
description: |~
{{ or .overrides.description "" | indent 4 }}
assignee:
name: {{ or .overrides.assignee "" }}
reporter:
name: {{ or .overrides.reporter .overrides.user }}
# watchers
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
- name: {{.}}{{end}}
- name:
`
const default_comment_template = `body: |~
{{ or .overrides.comment "" | indent 2 }}
`
const default_transition_template = `update:
comment:
- add:
body: |~
{{ or .overrides.comment "" | indent 10 }}
fields:{{if .meta.fields.assignee}}
assignee:
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}{{end}}{{if .meta.fields.components}}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
- name: {{ .name }}{{end}}{{end}}{{end}}{{if .meta.fields.description}}
description: {{or .overrides.description .fields.description }}{{end}}{{if .meta.fields.fixVersions}}{{if .meta.fields.fixVersions.allowedValues}}
fixVersions: # Values: {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
- name: {{.}}{{end}}{{end}}{{end}}{{end}}{{if .meta.fields.issuetype}}
issuetype: # Values: {{ range .meta.fields.issuetype.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}{{end}}{{if .meta.fields.labels}}
labels: {{range .fields.labels}}
- {{.}}{{end}}{{if .overrides.labels}}{{range (split "," .overrides.labels)}}
- {{.}}{{end}}{{end}}{{end}}{{if .meta.fields.priority}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority "unassigned" }}{{end}}{{if .meta.fields.reporter}}
reporter:
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}{{end}}{{if .meta.fields.resolution}}
resolution: # Values: {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}Fixed{{end}}{{end}}{{if .meta.fields.summary}}
summary: {{or .overrides.summary .fields.summary}}{{end}}{{if .meta.fields.versions.allowedValues}}
versions: # Values: {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
- name: {{.}}{{end}}{{end}}{{end}}
transition:
id: {{ .transition.id }}
name: {{ .transition.name }}
`
+586
View File
@@ -0,0 +1,586 @@
package jira
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/kballard/go-shellquote"
"gopkg.in/coryb/yaml.v2"
"gopkg.in/op/go-logging.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/data"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"time"
)
const VERSION = "0.1.7"
var log = logging.MustGetLogger("jira")
// Cli is go-jira client object
type Cli struct {
endpoint *url.URL
opts map[string]interface{}
cookieFile string
ua *http.Client
}
// New creates go-jira client object
func New(opts map[string]interface{}) *Cli {
homedir := homedir()
cookieJar, _ := cookiejar.New(nil)
endpoint, _ := opts["endpoint"].(string)
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
transport := &http.Transport{
TLSClientConfig: &tls.Config{},
}
if project, ok := opts["project"].(string); ok {
opts["project"] = strings.ToUpper(project)
}
if insecureSkipVerify, ok := opts["insecure"].(bool); ok {
transport.TLSClientConfig.InsecureSkipVerify = insecureSkipVerify
}
cli := &Cli{
endpoint: url,
opts: opts,
cookieFile: filepath.Join(homedir, ".jira.d", "cookies.js"),
ua: &http.Client{
Jar: cookieJar,
Transport: transport,
},
}
cli.ua.Jar.SetCookies(url, cli.loadCookies())
return cli
}
func (c *Cli) saveCookies(resp *http.Response) {
if _, ok := resp.Header["Set-Cookie"]; !ok {
return
}
cookies := resp.Cookies()
for _, cookie := range cookies {
if cookie.Domain == "" {
// if it is host:port then we need to split off port
parts := strings.Split(resp.Request.URL.Host, ":")
host := parts[0]
log.Debugf("Setting DOMAIN to %s for Cookie: %s", host, cookie)
cookie.Domain = host
}
}
// expiry in one week from now
expiry := time.Now().Add(24 * 7 * time.Hour)
for _, cookie := range cookies {
cookie.Expires = expiry
}
if currentCookies := c.loadCookies(); currentCookies != nil {
currentCookiesByName := make(map[string]*http.Cookie)
for _, cookie := range currentCookies {
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
}
for _, cookie := range cookies {
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
}
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
for _, v := range currentCookiesByName {
mergedCookies = append(mergedCookies, v)
}
jsonWrite(c.cookieFile, mergedCookies)
} else {
mkdir(path.Dir(c.cookieFile))
jsonWrite(c.cookieFile, cookies)
}
}
func (c *Cli) loadCookies() []*http.Cookie {
bytes, err := ioutil.ReadFile(c.cookieFile)
if err != nil && os.IsNotExist(err) {
// dont load cookies if the file does not exist
return nil
}
if err != nil {
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
panic(err)
}
cookies := []*http.Cookie{}
err = json.Unmarshal(bytes, &cookies)
if err != nil {
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
}
if os.Getenv("LOG_TRACE") != "" && log.IsEnabledFor(logging.DEBUG) {
log.Debugf("Loading Cookies: %s", cookies)
}
return cookies
}
func (c *Cli) post(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("POST", uri, content)
}
func (c *Cli) put(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("PUT", uri, content)
}
func (c *Cli) delete(uri string) (resp *http.Response, err error) {
method := "DELETE"
req, _ := http.NewRequest(method, uri, nil)
log.Infof("%s %s", req.Method, req.URL.String())
if resp, err = c.makeRequest(req); err != nil {
return nil, err
}
if resp.StatusCode == 401 {
if err = c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, nil)
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (resp *http.Response, err error) {
buffer := bytes.NewBufferString(content)
req, _ := http.NewRequest(method, uri, buffer)
log.Infof("%s %s", req.Method, req.URL.String())
if resp, err = c.makeRequest(req); err != nil {
return nil, err
}
if resp.StatusCode == 401 {
if err = c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) get(uri string) (resp *http.Response, err error) {
req, _ := http.NewRequest("GET", uri, nil)
log.Infof("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte, 0))
req.Write(logBuffer)
log.Debugf("%s", logBuffer)
}
if resp, err = c.makeRequest(req); err != nil {
return nil, err
}
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
// this is actually done in http.send but doing it
// here so we can log it in DumpRequest for debugging
for _, cookie := range c.ua.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
if log.IsEnabledFor(logging.DEBUG) {
out, _ := httputil.DumpRequest(req, true)
log.Debugf("Request: %s", out)
}
if resp, err = c.ua.Do(req); err != nil {
log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err)
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
log.Errorf("response status: %s", resp.Status)
}
runtime.SetFinalizer(resp, func(r *http.Response) {
r.Body.Close()
})
if _, ok := resp.Header["Set-Cookie"]; ok {
c.saveCookies(resp)
}
if log.IsEnabledFor(logging.DEBUG) {
out, _ := httputil.DumpResponse(resp, true)
log.Debugf("Response: %s", out)
}
return resp, nil
}
// GetTemplate will return the text/template for the given command name
func (c *Cli) GetTemplate(name string) string {
return c.getTemplate(name)
}
func getLookedUpTemplate(name string, dflt string) string {
if file, err := FindClosestParentPath(filepath.Join(".jira.d", "templates", name)); err == nil {
return readFile(file)
}
if _, err := os.Stat(fmt.Sprintf("/etc/go-jira/templates/%s", name)); err == nil {
file := fmt.Sprintf("/etc/go-jira/templates/%s", name)
return readFile(file)
}
return dflt
}
func (c *Cli) getTemplate(name string) string {
if override, ok := c.opts["template"].(string); ok {
if _, err := os.Stat(override); err == nil {
return readFile(override)
}
if t := getLookedUpTemplate(override, allTemplates[override]); t != "" {
return t
}
}
// create-bug etc are special, if we dont find it in the path
// then just return the create template
if strings.HasPrefix(name, "create-") {
return getLookedUpTemplate(name, c.getTemplate("create"))
}
return getLookedUpTemplate(name, allTemplates[name])
}
// NoChangesFound is an error returned from when editing templates
// and no modifications were made while editing
type NoChangesFound struct{}
func (f NoChangesFound) Error() string {
return "No changes found, aborting"
}
func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData map[string]interface{}, templateProcessor func(string) error) error {
tmpdir := filepath.Join(homedir(), ".jira.d", "tmp")
if err := mkdir(tmpdir); err != nil {
return err
}
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix)
if err != nil {
log.Errorf("Failed to make temp file in %s: %s", tmpdir, err)
return err
}
oldFileName := fh.Name()
tmpFileName := fmt.Sprintf("%s.yml", oldFileName)
// close tmpfile so we can rename on windows
fh.Close()
if err := os.Rename(oldFileName, tmpFileName); err != nil {
log.Errorf("Failed to rename %s to %s: %s", oldFileName, tmpFileName, err)
return err
}
fh, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_EXCL, 0600)
if err != nil {
log.Errorf("Failed to reopen temp file file in %s: %s", tmpFileName, err)
return err
}
defer fh.Close()
defer func() {
os.Remove(tmpFileName)
}()
err = runTemplate(template, templateData, fh)
if err != nil {
return err
}
fh.Close()
editor, ok := c.opts["editor"].(string)
if !ok {
editor = os.Getenv("JIRA_EDITOR")
if editor == "" {
editor = os.Getenv("EDITOR")
if editor == "" {
editor = "vim"
}
}
}
editing := c.getOptBool("edit", true)
tmpFileNameOrig := fmt.Sprintf("%s.orig", tmpFileName)
copyFile(tmpFileName, tmpFileNameOrig)
defer func() {
os.Remove(tmpFileNameOrig)
}()
for true {
if editing {
shell, _ := shellquote.Split(editor)
shell = append(shell, tmpFileName)
log.Debugf("Running: %#v", shell)
cmd := exec.Command(shell[0], shell[1:]...)
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
if err := cmd.Run(); err != nil {
log.Errorf("Failed to edit template with %s: %s", editor, err)
if promptYN("edit again?", true) {
continue
}
return err
}
diff := exec.Command("diff", "-q", tmpFileNameOrig, tmpFileName)
// if err == nil then diff found no changes
if err := diff.Run(); err == nil {
return NoChangesFound{}
}
}
edited := make(map[string]interface{})
var data []byte
if data, err = ioutil.ReadFile(tmpFileName); err != nil {
log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
if err := yaml.Unmarshal(data, &edited); err != nil {
log.Errorf("Failed to parse YAML: %s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
var fixed interface{}
if fixed, err = yamlFixup(edited); err != nil {
return err
}
edited = fixed.(map[string]interface{})
// if you want to abort editing a jira issue then
// you can add the "abort: true" flag to the document
// and we will abort now
if val, ok := edited["abort"].(bool); ok && val {
log.Infof("abort flag found in template, quiting")
return fmt.Errorf("abort flag found in template, quiting")
}
if _, ok := templateData["meta"]; ok {
mf := templateData["meta"].(map[string]interface{})["fields"]
if f, ok := edited["fields"].(map[string]interface{}); ok {
for k := range f {
if _, ok := mf.(map[string]interface{})[k]; !ok {
err := fmt.Errorf("Field %s is not editable", k)
log.Errorf("%s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
}
}
}
json, err := jsonEncode(edited)
if err != nil {
return err
}
if err := templateProcessor(json); err != nil {
log.Errorf("%s", err)
if editing && promptYN("edit again?", true) {
continue
}
}
return nil
}
return nil
}
// Browse will open up your default browser to the provided issue
func (c *Cli) Browse(issue string) error {
if val, ok := c.opts["browse"].(bool); ok && val {
if runtime.GOOS == "darwin" {
return exec.Command("open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
} else if runtime.GOOS == "linux" {
return exec.Command("xdg-open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
}
}
return nil
}
// SaveData will write out the yaml formated --saveFile file with provided data
func (c *Cli) SaveData(data interface{}) error {
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
yamlWrite(val, data)
}
return nil
}
// ViewIssueWorkLogs gets the worklog data for the given issue
func (c *Cli) ViewIssueWorkLogs(issue string) (interface{}, error) {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog", c.endpoint, issue)
data, err := responseToJSON(c.get(uri))
if err != nil {
return nil, err
}
return data, nil
}
// ViewIssue will return the details for the given issue id
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
if x := c.expansions(); len(x) > 0 {
uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ","))
}
data, err := responseToJSON(c.get(uri))
if err != nil {
return nil, err
}
return data, nil
}
type QueryOptions struct {
Assignee string
}
// FindIssues will return a list of issues that match the given options.
// If the "query" option is undefined it will generate a JQL query
// using any/all of the provide options: project, component, assignee,
// issuetype, watcher, reporter, sort
// Further it will restrict the fields being extracted from the jira
// response with the 'queryfields' option
func (c *Cli) FindIssues(sp SearchProvider) (*jiradata.SearchResults, error) {
req := sp.SearchRequest()
encoded, err := jsonEncode(req)
if err != nil {
return nil, err
}
uri := fmt.Sprintf("%s/reest/api/2/search", c.endpoint)
resp, err := c.post(uri, encoded)
if err != nil {
return nil, err
}
defer resp.Body.Close()
results := &jiradata.SearchResults{}
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(content, results)
if err != nil {
log.Errorf("JSON Parse Error: %s from %s", err, content)
}
return results, nil
}
// RankOrder type used to specify before/after ranking arguments to RankIssue
type RankOrder int
const (
// RANKBEFORE should be used to rank issue before the target issue
RANKBEFORE RankOrder = iota
// RANKAFTER should be used to rank issue after the target issue
RANKAFTER RankOrder = iota
)
// RankIssue will modify issue to have rank before or after the target issue
func (c *Cli) RankIssue(issue, target string, order RankOrder) error {
type RankRequest struct {
Issues []string `json:"issues"`
Before string `json:"rankBeforeIssue,omitempty"`
After string `json:"rankAfterIssue,omitempty"`
}
req := &RankRequest{
Issues: []string{
issue,
},
}
if order == RANKBEFORE {
req.Before = target
} else {
req.After = target
}
json, err := jsonEncode(req)
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/agile/1.0/issue/rank", c.endpoint)
if c.getOptBool("dryrun", false) {
log.Debugf("PUT: %s", json)
log.Debugf("Dryrun mode, skipping PUT")
return nil
}
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode != 204 {
return fmt.Errorf("failed to modify issue rank: %s", resp.Status)
}
return nil
}
// GetOptString will extract the string from the Cli object options
// otherwise return the provided default
func (c *Cli) GetOptString(optName string, dflt string) string {
return c.getOptString(optName, dflt)
}
func (c *Cli) getOptString(optName string, dflt string) string {
if val, ok := c.opts[optName].(string); ok {
return val
}
return dflt
}
// GetOptBool will extract the boolean value from the Client object options
// otherwise return the provided default\
func (c *Cli) GetOptBool(optName string, dflt bool) bool {
return c.getOptBool(optName, dflt)
}
func (c *Cli) getOptBool(optName string, dflt bool) bool {
if val, ok := c.opts[optName].(bool); ok {
return val
}
return dflt
}
// expansions returns a comma-separated list of values for field expansion
func (c *Cli) expansions() []string {
var expansions []string
if x, ok := c.opts["expand"].(string); ok {
expansions = strings.Split(x, ",")
}
return expansions
}
+1017
View File
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
package jira
import (
"gopkg.in/Netflix-Skunkworks/go-jira.v1/data"
"bytes"
"fmt"
"strings"
)
type SearchProvider interface {
SearchRequest() *jiradata.SearchRequest
}
type SearchOptions struct {
Assignee string
Query string
QueryFields string
Project string
Component string
IssueType string
Watcher string
Reporter string
Sort string
MaxResults int
}
func (o *SearchOptions) SearchRequest() *jiradata.SearchRequest {
req := &jiradata.SearchRequest{}
if o.Query == "" {
qbuff := bytes.NewBufferString("resolution = unresolved")
if o.Project == "" {
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", o.Project))
}
if o.Component != "" {
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", o.Component))
}
if o.Assignee != "" {
qbuff.WriteString(fmt.Sprintf(" AND assignee = '%s'", o.Assignee))
}
if o.IssueType != "" {
qbuff.WriteString(fmt.Sprintf(" AND issuetype = '%s'", o.IssueType))
}
if o.Watcher != "" {
qbuff.WriteString(fmt.Sprintf(" AND watcher = '%s'", o.Watcher))
}
if o.Reporter != "" {
qbuff.WriteString(fmt.Sprintf(" AND reporter = '%s'", o.Reporter))
}
if o.Sort != "" {
qbuff.WriteString(fmt.Sprintf(" ORDER BY %s", o.Sort))
}
req.JQL = qbuff.String()
} else {
req.JQL = o.Query
}
req.Fields = append(req.Fields, "summary")
if o.QueryFields != "" {
fields := strings.Split(o.QueryFields, ",")
req.Fields = append(req.Fields, fields...)
}
req.StartAt = 0
req.MaxResults = o.MaxResults
return req
}
+220
View File
@@ -0,0 +1,220 @@
package jira
var allTemplates = map[string]string{
"debug": defaultDebugTemplate,
"fields": defaultDebugTemplate,
"editmeta": defaultDebugTemplate,
"transmeta": defaultDebugTemplate,
"createmeta": defaultDebugTemplate,
"issuelinktypes": defaultDebugTemplate,
"list": defaultListTemplate,
"table": defaultTableTemplate,
"view": defaultViewTemplate,
"edit": defaultEditTemplate,
"transitions": defaultTransitionsTemplate,
"components": defaultComponentsTemplate,
"issuetypes": defaultIssuetypesTemplate,
"create": defaultCreateTemplate,
"comment": defaultCommentTemplate,
"transition": defaultTransitionTemplate,
"request": defaultDebugTemplate,
"worklog": defaultWorklogTemplate,
"worklogs": defaultWorklogsTemplate,
}
const defaultDebugTemplate = "{{ . | toJson}}\n"
const defaultListTemplate = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
const defaultTableTemplate = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
| {{ "Issue" | printf "%-14s" }} | {{ "Summary" | printf "%-55s" }} | {{ "Priority" | printf "%-12s" }} | {{ "Status" | printf "%-12s" }} | {{ "Age" | printf "%-10s" }} | {{ "Reporter" | printf "%-12s" }} | {{ "Assignee" | printf "%-12s" }} |
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
{{ range .issues -}}
| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned> {{end}} |
{{ end -}}
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
`
const defaultViewTemplate = `{{/* view template */ -}}
issue: {{ .key }}
{{if .fields.created -}}
created: {{ .fields.created | age }} ago
{{end -}}
{{if .fields.status -}}
status: {{ .fields.status.name }}
{{end -}}
summary: {{ .fields.summary }}
project: {{ .fields.project.key }}
{{if .fields.components -}}
components: {{ range .fields.components }}{{ .name }} {{end}}
{{end -}}
{{if .fields.issuetype -}}
issuetype: {{ .fields.issuetype.name }}
{{end -}}
{{if .fields.assignee -}}
assignee: {{ .fields.assignee.name }}
{{end -}}
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}
{{if .fields.customfield_10110 -}}
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
{{end -}}
{{if .fields.issuelinks -}}
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}[{{.outwardIssue.fields.status.name}}]{{end}}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}
{{end -}}
{{if .fields.priority -}}
priority: {{ .fields.priority.name }}
{{end -}}
{{if .fields.votes -}}
votes: {{ .fields.votes.votes}}
{{end -}}
{{if .fields.labels -}}
labels: {{ join ", " .fields.labels }}
{{end -}}
description: |
{{ or .fields.description "" | indent 2 }}
{{if .fields.comment.comments}}
comments:
{{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
{{ or .body "" | indent 4}}
{{end}}
{{end -}}
`
const defaultEditTemplate = `{{/* edit template */ -}}
# issue: {{ .key }}
update:
comment:
- add:
body: |~
{{ or .overrides.comment "" | indent 10 }}
fields:
summary: {{ or .overrides.summary .fields.summary }}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
- name: {{ .name }}{{end}}{{end}}
assignee:
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
reporter:
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
# watchers
customfield_10110: {{ range .fields.customfield_10110 }}
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
- name: {{ .overrides.watcher}}{{end}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority .fields.priority.name }}
description: |~
{{ or .overrides.description (or .fields.description "") | indent 4 }}
# comments:
# {{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
# {{ or .body "" | indent 4 | comment}}
# {{end}}
`
const defaultTransitionsTemplate = `{{ range .transitions }}{{.id }}: {{.name}}
{{end}}`
const defaultComponentsTemplate = `{{ range . }}{{.id }}: {{.name}}
{{end}}`
const defaultIssuetypesTemplate = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
{{end}}{{end}}`
const defaultCreateTemplate = `{{/* create template */ -}}
fields:
project:
key: {{ or .overrides.project "" }}
issuetype:
name: {{ or .overrides.issuetype "" }}
summary: {{ or .overrides.summary "" }}{{if .meta.fields.priority.allowedValues}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority ""}}{{end}}{{if .meta.fields.components.allowedValues}}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
- name: {{ . }}{{end}}{{end}}
description: |~
{{ or .overrides.description "" | indent 4 }}{{if .meta.fields.assignee}}
assignee:
name: {{ or .overrides.assignee "" }}{{end}}{{if .meta.fields.reporter}}
reporter:
name: {{ or .overrides.reporter .overrides.user }}{{end}}{{if .meta.fields.customfield_10110}}
# watchers
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
- name: {{.}}{{end}}
- name:{{end}}`
const defaultCommentTemplate = `body: |~
{{ or .overrides.comment "" | indent 2 }}
`
const defaultTransitionTemplate = `{{/* transition template */ -}}
update:
comment:
- add:
body: |~
{{ or .overrides.comment "" | indent 10 }}
fields:
{{- if .meta.fields.assignee}}
assignee:
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.components}}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
- name: {{ .name }}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.description}}
description: {{or .overrides.description .fields.description }}
{{- end -}}
{{if .meta.fields.fixVersions -}}
{{if .meta.fields.fixVersions.allowedValues}}
fixVersions: # Values: {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
- name: {{.}}{{end}}{{end}}
{{- end -}}
{{- end -}}
{{if .meta.fields.issuetype}}
issuetype: # Values: {{ range .meta.fields.issuetype.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.labels}}
labels: {{range .fields.labels}}
- {{.}}{{end}}{{if .overrides.labels}}{{range (split "," .overrides.labels)}}
- {{.}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.priority}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority "unassigned" }}
{{- end -}}
{{if .meta.fields.reporter}}
reporter:
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.resolution}}
resolution: # Values: {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}{{or .overrides.defaultResolution "Fixed"}}{{end}}
{{- end -}}
{{if .meta.fields.summary}}
summary: {{or .overrides.summary .fields.summary}}
{{- end -}}
{{if .meta.fields.versions.allowedValues}}
versions: # Values: {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
- name: {{.}}{{end}}{{end}}
{{- end}}
transition:
id: {{ .transition.id }}
name: {{ .transition.name }}
`
const defaultWorklogTemplate = `{{/* worklog template */ -}}
# issue: {{ .issue }}
comment: |~
{{ or .comment "" }}
timeSpent: {{ or .timeSpent "" }}
started:
`
const defaultWorklogsTemplate = `{{/* worklogs template */ -}}
{{ range .worklogs }}- # {{.author.name}}, {{.created | age}} ago
comment: {{ or .comment "" }}
timeSpent: {{ .timeSpent }}
{{end}}`
+123 -61
View File
@@ -1,63 +1,79 @@
package cli
package jira
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/mgutz/ansi"
"gopkg.in/coryb/yaml.v2"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"text/template"
"time"
)
func homedir() string {
if runtime.GOOS == "windows" {
return os.Getenv("USERPROFILE")
}
return os.Getenv("HOME")
}
// FindParentPaths will find all available paths from the current path up to the root
// that matches the given fileName path
func FindParentPaths(fileName string) []string {
cwd, _ := os.Getwd()
paths := make([]string, 0)
paths := []string{}
// special case if homedir is not in current path then check there anyway
homedir := os.Getenv("HOME")
if !strings.HasPrefix(cwd, homedir) {
file := fmt.Sprintf("%s/%s", homedir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, file)
homedir := homedir()
if !filepath.HasPrefix(cwd, homedir) {
path := filepath.Join(homedir, fileName)
if _, err := os.Stat(path); err == nil {
paths = append(paths, path)
}
}
var dir string
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
if dir == "/" {
dir = fmt.Sprintf("/%s", part)
} else {
dir = fmt.Sprintf("%s/%s", dir, part)
path := filepath.Join(cwd, fileName)
if _, err := os.Stat(path); err == nil {
paths = append(paths, path)
}
for true {
cwd = filepath.Dir(cwd)
path := filepath.Join(cwd, fileName)
if _, err := os.Stat(path); err == nil {
paths = append(paths, path)
}
file := fmt.Sprintf("%s/%s", dir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, file)
if cwd[len(cwd)-1] == filepath.Separator {
break
}
}
return paths
}
// FindClosestParentPath finds the path that matches the given fileName path that is
// closest to the current working directory
func FindClosestParentPath(fileName string) (string, error) {
paths := FindParentPaths(fileName)
if len(paths) > 0 {
return paths[len(paths)-1], nil
}
return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
return "", fmt.Errorf("%s not found in parent directory hierarchy", fileName)
}
func readFile(file string) string {
var bytes []byte
var err error
log.Debugf("readFile: reading %q", file)
if bytes, err = ioutil.ReadFile(file); err != nil {
log.Error("Failed to read file %s: %s", file, err)
log.Errorf("Failed to read file %s: %s", file, err)
os.Exit(1)
}
return string(bytes)
@@ -79,40 +95,52 @@ func copyFile(src, dst string) (err error) {
}
func fuzzyAge(start string) (string, error) {
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil {
t, err := time.Parse("2006-01-02T15:04:05.000-0700", start)
if err != nil {
return "", err
} else {
delta := time.Now().Sub(t)
if delta.Minutes() < 2 {
return "a minute", nil
} else if dm := delta.Minutes(); dm < 45 {
return fmt.Sprintf("%d minutes", int(dm)), nil
} else if dm := delta.Minutes(); dm < 90 {
return "an hour", nil
} else if dh := delta.Hours(); dh < 24 {
return fmt.Sprintf("%d hours", int(dh)), nil
} else if dh := delta.Hours(); dh < 48 {
return "a day", nil
} else {
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
}
}
return "unknown", nil
delta := time.Now().Sub(t)
if delta.Minutes() < 2 {
return "a minute", nil
} else if dm := delta.Minutes(); dm < 45 {
return fmt.Sprintf("%d minutes", int(dm)), nil
} else if dm := delta.Minutes(); dm < 90 {
return "an hour", nil
} else if dh := delta.Hours(); dh < 24 {
return fmt.Sprintf("%d hours", int(dh)), nil
} else if dh := delta.Hours(); dh < 48 {
return "a day", nil
}
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
}
func dateFormat(format string, content string) (string, error) {
t, err := time.Parse("2006-01-02T15:04:05.000-0700", content)
if err != nil {
return "", err
}
return t.Format(format), nil
}
// RunTemplate will run the give templateContent as a golang text/template
// and pass the provided data to the template execution. It will write
// the output to the provided "out" writer.
func RunTemplate(templateContent string, data interface{}, out io.Writer) error {
return runTemplate(templateContent, data, out)
}
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
if out == nil {
out = os.Stdout
}
funcs := map[string]interface{}{
"toJson": func(content interface{}) (string, error) {
if bytes, err := json.MarshalIndent(content, "", " "); err != nil {
bytes, err := json.MarshalIndent(content, "", " ")
if err != nil {
return "", err
} else {
return string(bytes), nil
}
return string(bytes), nil
},
"append": func(more string, content interface{}) (string, error) {
switch value := content.(type) {
@@ -121,13 +149,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
case []byte:
return string(append(content.([]byte), []byte(more)...)), nil
default:
return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
return "", fmt.Errorf("Unknown type: %s", value)
}
},
"indent": func(spaces int, content string) string {
indent := make([]rune, spaces+1, spaces+1)
indent[0] = '\n'
for i := 1; i < spaces+1; i += 1 {
for i := 1; i < spaces+1; i++ {
indent[i] = ' '
}
@@ -152,6 +180,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
"split": func(sep string, content string) []string {
return strings.Split(content, sep)
},
"join": func(sep string, content []interface{}) string {
vals := make([]string, len(content))
for i, v := range content {
vals[i] = v.(string)
}
return strings.Join(vals, sep)
},
"abbrev": func(max int, content string) string {
if len(content) > max {
var buffer bytes.Buffer
@@ -163,7 +198,7 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
},
"rep": func(count int, content string) string {
var buffer bytes.Buffer
for i := 0; i < count; i += 1 {
for i := 0; i < count; i++ {
buffer.WriteString(content)
}
return buffer.String()
@@ -171,20 +206,23 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
"age": func(content string) (string, error) {
return fuzzyAge(content)
},
"dateFormat": func(format string, content string) (string, error) {
return dateFormat(format, content)
},
}
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
log.Error("Failed to parse template: %s", err)
tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent)
if err != nil {
log.Errorf("Failed to parse template: %s", err)
return err
}
if err := tmpl.Execute(out, data); err != nil {
log.Errorf("Failed to execute template: %s", err)
return err
} else {
if err := tmpl.Execute(out, data); err != nil {
log.Error("Failed to execute template: %s", err)
return err
}
}
return nil
}
func responseToJson(resp *http.Response, err error) (interface{}, error) {
func responseToJSON(resp *http.Response, err error) (interface{}, error) {
if err != nil {
return nil, err
}
@@ -193,7 +231,7 @@ func responseToJson(resp *http.Response, err error) (interface{}, error) {
if resp.StatusCode == 400 {
if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
for _, errMsg := range val.([]interface{}) {
log.Error("%s", errMsg)
log.Errorf("%s", errMsg)
}
}
}
@@ -206,7 +244,7 @@ func jsonDecode(io io.Reader) interface{} {
var data interface{}
err = json.Unmarshal(content, &data)
if err != nil {
log.Error("JSON Parse Error: %s from %s", err, content)
log.Errorf("JSON Parse Error: %s from %s", err, content)
}
return data
}
@@ -217,7 +255,7 @@ func jsonEncode(data interface{}) (string, error) {
err := enc.Encode(data)
if err != nil {
log.Error("Failed to encode data %s: %s", data, err)
log.Errorf("Failed to encode data %s: %s", data, err)
return "", err
}
return buffer.String(), nil
@@ -227,13 +265,28 @@ func jsonWrite(file string, data interface{}) {
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
defer fh.Close()
if err != nil {
log.Error("Failed to open %s: %s", file, err)
log.Errorf("Failed to open %s: %s", file, err)
os.Exit(1)
}
enc := json.NewEncoder(fh)
enc.Encode(data)
}
func yamlWrite(file string, data interface{}) {
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
defer fh.Close()
if err != nil {
log.Errorf("Failed to open %s: %s", file, err)
os.Exit(1)
}
if out, err := yaml.Marshal(data); err != nil {
log.Errorf("Failed to marshal yaml %v: %s", data, err)
os.Exit(1)
} else {
fh.Write(out)
}
}
func promptYN(prompt string, yes bool) bool {
reader := bufio.NewReader(os.Stdin)
if !yes {
@@ -269,10 +322,13 @@ func yamlFixup(data interface{}) (interface{}, error) {
}
default:
err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
log.Error("%s", err)
log.Errorf("%s", err)
return nil, err
}
}
if len(copy) == 0 {
return nil, nil
}
return copy, nil
case map[string]interface{}:
copy := make(map[string]interface{})
@@ -283,6 +339,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
copy[k] = fixed
}
}
if len(copy) == 0 {
return nil, nil
}
return copy, nil
case []interface{}:
copy := make([]interface{}, 0, len(d))
@@ -293,9 +352,12 @@ func yamlFixup(data interface{}) (interface{}, error) {
copy = append(copy, fixed)
}
}
if len(copy) == 0 {
return nil, nil
}
return copy, nil
case string:
if d == "" {
if d == "" || d == "\n" {
return nil, nil
}
return d, nil
@@ -306,16 +368,16 @@ func yamlFixup(data interface{}) (interface{}, error) {
func mkdir(dir string) error {
if stat, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
log.Error("Failed to stat %s: %s", dir, err)
log.Errorf("Failed to stat %s: %s", dir, err)
return err
} else if err == nil && !stat.IsDir() {
err := fmt.Errorf("%s exists and is not a directory!", dir)
log.Error("%s", err)
err := fmt.Errorf("%s exists and is not a directory", dir)
log.Errorf("%s", err)
return err
} else {
// dir does not exist, so try to create it
if err := os.MkdirAll(dir, 0755); err != nil {
log.Error("Failed to mkdir -p %s: %s", dir, err)
log.Errorf("Failed to mkdir -p %s: %s", dir, err)
return err
}
}
+257 -70
View File
@@ -3,33 +3,29 @@ package main
import (
"bytes"
"fmt"
"github.com/Netflix-Skunkworks/go-jira/jira/cli"
"github.com/coryb/optigo"
"github.com/op/go-logging"
"gopkg.in/coryb/yaml.v2"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/coryb/optigo"
logging "github.com/op/go-logging"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/cli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/lib"
"gopkg.in/coryb/yaml.v2"
)
var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
var (
log = logging.MustGetLogger("jira")
)
func main() {
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
logging.SetBackend(
logging.NewBackendFormatter(
logBackend,
logging.MustStringFormatter(format),
),
)
logging.SetLevel(logging.NOTICE, "")
jiracli.InitLogging()
user := os.Getenv("USER")
home := os.Getenv("HOME")
defaultQueryFields := "summary,created,priority,status,reporter,assignee"
defaultSort := "priority asc, created"
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
defaultSort := "priority asc, key"
defaultMaxResults := 500
usage := func(ok bool) {
@@ -48,59 +44,87 @@ func main() {
}
output := fmt.Sprintf(`
Usage:
jira (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER]) [-f FIELDS] [-s ORDER] [--max_results MAX_RESULTS]
jira (ls|list) <Query Options>
jira view ISSUE
jira edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
jira create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
jira worklog ISSUE
jira add worklog ISSUE <Worklog Options>
jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
jira create [--noedit] [-p PROJECT] <Create Options>
jira DUPLICATE dups ISSUE
jira BLOCKER blocks ISSUE
jira watch ISSUE [-w WATCHER]
jira (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
jira ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
jira comment ISSUE [-m COMMENT]
jira vote ISSUE [--down]
jira rank ISSUE (after|before) ISSUE
jira watch ISSUE [-w WATCHER] [--remove]
jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
jira ack ISSUE [--edit] <Edit Options>
jira close ISSUE [--edit] <Edit Options>
jira resolve ISSUE [--edit] <Edit Options>
jira reopen ISSUE [--edit] <Edit Options>
jira start ISSUE [--edit] <Edit Options>
jira stop ISSUE [--edit] <Edit Options>
jira todo ISSUE [--edit] <Edit Options>
jira backlog ISSUE [--edit] <Edit Options>
jira done ISSUE [--edit] <Edit Options>
jira prog|progress|in-progress [--edit] <Edit Options>
jira comment ISSUE [--noedit] <Edit Options>
jira (set,add,remove) labels ISSUE [LABEL] ...
jira take ISSUE
jira (assign|give) ISSUE ASSIGNEE
jira fields
jira issuelinktypes
jira transmeta ISSUE
jira editmeta ISSUE
jira add component [-p PROJECT] NAME DESCRIPTION LEAD
jira components [-p PROJECT]
jira issuetypes [-p PROJECT]
jira createmeta [-p PROJECT] [-i ISSUETYPE]
jira transitions ISSUE
jira export-templates [-d DIR] [-t template]
jira (b|browse) ISSUE
jira login
jira logout
jira request [-M METHOD] URI [DATA]
jira ISSUE
General Options:
-b --browse Open your browser to the Jira issue
-e --endpoint=URI URI to use for jira
-k --insecure disable TLS certificate verification
-h --help Show this usage
-t --template=FILE Template file to use for output/editing
-u --user=USER Username to use for authenticaion (default: %s)
-v --verbose Increase output logging
--version Print version
Command Options:
Query Options:
-a --assignee=USER Username assigned the issue
-b --browse Open your browser to the Jira issue
-c --component=COMPONENT Component to Search for
-d --directory=DIR Directory to export templates to (default: %s)
-f --queryfields=FIELDS Fields that are used in "list" template: (default: %s)
-i --issuetype=ISSUETYPE Jira Issue Type (default: Bug)
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
-i --issuetype=ISSUETYPE The Issue Type
-l --limit=VAL Maximum number of results to return in query (default: %d)
-p --project=PROJECT Project to Search for
-q --query=JQL Jira Query Language expression for the search
-r --reporter=USER Reporter to search for
-s --sort=ORDER For list operations, sort issues (default: %s)
-w --watcher=USER Watcher to add to issue (default: %s)
or Watcher to search for
--max_results=VAL Maximum number of results to return in query (default: %d)
`, user, fmt.Sprintf("%s/.jira.d/templates", home), defaultQueryFields, defaultSort, user, defaultMaxResults)
Edit Options:
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
Create Options:
-i --issuetype=ISSUETYPE Jira Issue Type (default: Bug)
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
Worklog Options:
-T --time-spent=TIMESPENT Time spent working on issue
-m --comment=COMMENT Comment message for worklog
Command Options:
-d --directory=DIR Directory to export templates to (default: %s)
`, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home))
printer(output)
}
@@ -122,7 +146,17 @@ Command Options:
"reopen": "reopen",
"start": "start",
"stop": "stop",
"todo": "todo",
"backlog": "backlog",
"done": "done",
"prog": "in-progress",
"progress": "in-progress",
"in-progress": "in-progress",
"comment": "comment",
"label": "labels",
"labels": "labels",
"component": "component",
"components": "components",
"take": "take",
"assign": "assign",
"give": "assign",
@@ -136,6 +170,13 @@ Command Options:
"export-templates": "export-templates",
"browse": "browse",
"login": "login",
"logout": "logout",
"req": "request",
"request": "request",
"vote": "vote",
"rank": "rank",
"worklog": "worklog",
"addworklog": "addworklog",
}
defaults := map[string]interface{}{
@@ -144,25 +185,30 @@ Command Options:
"directory": fmt.Sprintf("%s/.jira.d/templates", home),
"sort": defaultSort,
"max_results": defaultMaxResults,
"method": "GET",
"quiet": false,
}
opts := make(map[string]interface{})
overrides := make(map[string]string)
setopt := func(name string, value interface{}) {
opts[name] = value
}
op := optigo.NewDirectAssignParser(map[string]interface{}{
"h|help": usage,
"version": func() {
fmt.Println(fmt.Sprintf("version: %s", jira.VERSION))
os.Exit(0)
},
"v|verbose+": func() {
logging.SetLevel(logging.GetLevel("")+1, "")
jiracli.VerboseLogging()
},
"dryrun": setopt,
"b|browse": setopt,
"editor=s": setopt,
"u|user=s": setopt,
"endpoint=s": setopt,
"k|insecure": setopt,
"t|template=s": setopt,
"q|query=s": setopt,
"p|project=s": setopt,
@@ -170,31 +216,38 @@ Command Options:
"a|assignee=s": setopt,
"i|issuetype=s": setopt,
"w|watcher=s": setopt,
"remove": setopt,
"r|reporter=s": setopt,
"f|queryfields=s": setopt,
"x|expand=s": setopt,
"s|sort=s": setopt,
"l|limit|max_results=i": setopt,
"o|override=s%": &overrides,
"o|override=s%": &opts,
"noedit": setopt,
"edit": setopt,
"m|comment=s": setopt,
"d|dir|directory=s": setopt,
"M|method=s": setopt,
"S|saveFile=s": setopt,
"T|time-spent=s": setopt,
"Q|quiet": setopt,
"down": setopt,
})
if err := op.ProcessAll(os.Args[1:]); err != nil {
log.Error("%s", err)
log.Errorf("%s", err)
usage(false)
}
args := op.Args
opts["overrides"] = overrides
command := "view"
var command string
if len(args) > 0 {
if alias, ok := jiraCommands[args[0]]; ok {
command = alias
args = args[1:]
} else if len(args) > 1 {
// look at second arg for "dups" and "blocks" commands
// also for 'set/add/remove' actions like 'labels'
if alias, ok := jiraCommands[args[1]]; ok {
command = alias
args = append(args[:1], args[2:]...)
@@ -202,51 +255,75 @@ Command Options:
}
}
if command == "" && len(args) > 0 {
command = args[0]
args = args[1:]
}
os.Setenv("JIRA_OPERATION", command)
loadConfigs(opts)
// check to see if it was set in the configs:
if value, ok := opts["command"].(string); ok {
command = value
} else if _, ok := jiraCommands[command]; !ok || command == "" {
if command != "" {
args = append([]string{command}, args...)
}
command = "view"
}
// apply defaults
for k, v := range defaults {
if _, ok := opts[k]; !ok {
log.Debug("Setting %q to %#v from defaults", k, v)
log.Debugf("Setting %q to %#v from defaults", k, v)
opts[k] = v
}
}
log.Debug("opts: %v", opts)
log.Debug("args: %v", args)
log.Debugf("opts: %v", opts)
log.Debugf("args: %v", args)
if _, ok := opts["endpoint"]; !ok {
log.Error("endpoint option required. Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
log.Errorf("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
os.Exit(1)
}
c := cli.New(opts)
c := jira.New(opts)
log.Debug("opts: %s", opts)
log.Debugf("opts: %s", opts)
setEditing := func(dflt bool) {
log.Debug("Default Editing: %t", dflt)
log.Debugf("Default Editing: %t", dflt)
if dflt {
if val, ok := opts["noedit"].(bool); ok && val {
log.Debug("Setting edit = false")
log.Debugf("Setting edit = false")
opts["edit"] = false
} else {
log.Debug("Setting edit = true")
log.Debugf("Setting edit = true")
opts["edit"] = true
}
} else {
if _, ok := opts["edit"].(bool); !ok {
log.Debug("Setting edit = %t", dflt)
log.Debugf("Setting edit = %t", dflt)
opts["edit"] = dflt
}
}
}
requireArgs := func(count int) {
if len(args) < count {
log.Errorf("Not enough arguments. %d required, %d provided", count, len(args))
usage(false)
}
}
var err error
switch command {
case "login":
err = c.CmdLogin()
case "logout":
err = c.CmdLogout()
case "fields":
err = c.CmdFields()
case "list":
@@ -257,12 +334,12 @@ Command Options:
err = c.CmdEdit(args[0])
} else {
var data interface{}
if data, err = c.FindIssues(); err == nil {
if data, err = c.FindIssues(&jira.SearchOptions{}); err == nil {
issues := data.(map[string]interface{})["issues"].([]interface{})
for _, issue := range issues {
if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil {
switch err.(type) {
case cli.NoChangesFound:
case jira.NoChangesFound:
log.Warning("No Changes found: %s", err)
err = nil
continue
@@ -273,8 +350,10 @@ Command Options:
}
}
case "editmeta":
requireArgs(1)
err = c.CmdEditMeta(args[0])
case "transmeta":
requireArgs(1)
err = c.CmdTransitionMeta(args[0])
case "issuelinktypes":
err = c.CmdIssueLinkTypes()
@@ -286,55 +365,161 @@ Command Options:
setEditing(true)
err = c.CmdCreate()
case "transitions":
requireArgs(1)
err = c.CmdTransitions(args[0])
case "blocks":
requireArgs(2)
err = c.CmdBlocks(args[0], args[1])
case "dups":
setEditing(true)
requireArgs(2)
if err = c.CmdDups(args[0], args[1]); err == nil {
opts["resolution"] = "Duplicate"
err = c.CmdTransition(args[0], "close")
trans, err := c.ValidTransitions(args[0])
if err == nil {
if trans.Find("close") != nil {
err = c.CmdTransition(args[0], "close")
} else if trans.Find("done") != nil {
// for now just assume if there is no "close", then
// there is a "done" state
err = c.CmdTransition(args[0], "done")
} else if trans.Find("start") != nil {
err = c.CmdTransition(args[0], "start")
if err == nil {
err = c.CmdTransition(args[0], "stop")
}
}
}
}
case "watch":
err = c.CmdWatch(args[0])
requireArgs(1)
watcher := c.GetOptString("watcher", opts["user"].(string))
remove := c.GetOptBool("remove", false)
err = c.CmdWatch(args[0], watcher, remove)
case "transition":
requireArgs(2)
setEditing(true)
err = c.CmdTransition(args[0], args[1])
err = c.CmdTransition(args[1], args[0])
case "close":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "close")
case "acknowledge":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "acknowledge")
case "reopen":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "reopen")
case "resolve":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "resolve")
case "start":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "start")
case "stop":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "stop")
case "todo":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "To Do")
case "backlog":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "Backlog")
case "done":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "Done")
case "in-progress":
requireArgs(1)
setEditing(false)
err = c.CmdTransition(args[0], "Progress")
case "comment":
requireArgs(1)
setEditing(true)
err = c.CmdComment(args[0])
case "labels":
requireArgs(2)
action := args[0]
issue := args[1]
labels := args[2:]
err = c.CmdLabels(action, issue, labels)
case "component":
requireArgs(2)
action := args[0]
project := opts["project"].(string)
name := args[1]
var lead string
var description string
if len(args) > 2 {
description = args[2]
}
if len(args) > 3 {
lead = args[2]
}
err = c.CmdComponent(action, project, name, description, lead)
case "components":
project := opts["project"].(string)
err = c.CmdComponents(project)
case "take":
requireArgs(1)
err = c.CmdAssign(args[0], opts["user"].(string))
case "browse":
requireArgs(1)
opts["browse"] = true
err = c.Browse(args[0])
case "export-tempaltes":
case "export-templates":
err = c.CmdExportTemplates()
case "assign":
requireArgs(2)
err = c.CmdAssign(args[0], args[1])
default:
case "view":
requireArgs(1)
err = c.CmdView(args[0])
case "worklog":
if len(args) > 0 && args[0] == "add" {
setEditing(true)
requireArgs(2)
err = c.CmdWorklog(args[0], args[1])
} else {
requireArgs(1)
err = c.CmdWorklogs(args[0])
}
case "vote":
requireArgs(1)
if val, ok := opts["down"]; ok {
err = c.CmdVote(args[0], !val.(bool))
} else {
err = c.CmdVote(args[0], true)
}
case "rank":
requireArgs(3)
if args[1] == "after" {
err = c.CmdRankAfter(args[0], args[2])
} else {
err = c.CmdRankBefore(args[0], args[2])
}
case "request":
requireArgs(1)
data := ""
if len(args) > 1 {
data = args[1]
}
err = c.CmdRequest(args[0], data)
default:
log.Errorf("Unknown command %s", command)
os.Exit(1)
}
if err != nil {
log.Error("%s", err)
log.Errorf("%s", err)
os.Exit(1)
}
os.Exit(0)
@@ -342,8 +527,10 @@ Command Options:
func parseYaml(file string, opts map[string]interface{}) {
if fh, err := ioutil.ReadFile(file); err == nil {
log.Debug("Found Config file: %s", file)
yaml.Unmarshal(fh, &opts)
log.Debugf("Found Config file: %s", file)
if err := yaml.Unmarshal(fh, &opts); err != nil {
log.Errorf("Unable to parse %s: %s", file, err)
}
}
}
@@ -369,12 +556,12 @@ func populateEnv(opts map[string]interface{}) {
func loadConfigs(opts map[string]interface{}) {
populateEnv(opts)
paths := cli.FindParentPaths(".jira.d/config.yml")
paths := jira.FindParentPaths(".jira.d/config.yml")
// prepend
paths = append([]string{"/etc/jira-cli.yml"}, paths...)
paths = append(paths, "/etc/go-jira.yml")
// iterate paths in reverse
for i := len(paths) - 1; i >= 0; i-- {
for i := 0; i < len(paths); i++ {
file := paths[i]
if stat, err := os.Stat(file); err == nil {
tmp := make(map[string]interface{})
@@ -382,21 +569,21 @@ func loadConfigs(opts map[string]interface{}) {
if stat.Mode()&0111 == 0 {
parseYaml(file, tmp)
} else {
log.Debug("Found Executable Config file: %s", file)
log.Debugf("Found Executable Config file: %s", file)
// it is executable, so run it and try to parse the output
cmd := exec.Command(file)
stdout := bytes.NewBufferString("")
cmd.Stdout = stdout
cmd.Stderr = bytes.NewBufferString("")
if err := cmd.Run(); err != nil {
log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
log.Errorf("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
os.Exit(1)
}
yaml.Unmarshal(stdout.Bytes(), &tmp)
}
for k, v := range tmp {
if _, ok := opts[k]; !ok {
log.Debug("Setting %q to %#v from %s", k, v, file)
log.Debugf("Setting %q to %#v from %s", k, v, file)
opts[k] = v
}
}
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python
from lxml import html
import requests
import json
page = requests.get('https://docs.atlassian.com/jira/REST/cloud')
tree = html.fromstring(page.content)
schemas = tree.xpath("//div[@class='representation-doc-block']//code/text()")
for schema in schemas:
try:
data = json.loads(schema)
if "title" in data:
title = data["title"].replace(" ", "")
print "Writing {}.json".format(title)
with open("{}.json".format(title), 'w') as f:
f.write(schema)
except:
True
+1
View File
@@ -0,0 +1 @@
!src/
+2
View File
@@ -0,0 +1,2 @@
endpoint: http://localhost:8080
user: gojira
Executable
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --user admin"
PLAN 15
# clean out any old containers
docker rm -f go-jira-test
RUNS docker build . -t go-jira-test
mkdir -p $(pwd)/.maven-cache
# start newt jira service, cache the users m2 directory to make startup faster
RUNS docker run --detach -v $(pwd)/.maven-cache:/root/.m2/repository --name go-jira-test --publish 8080:8080 go-jira-test:latest
# wait for docker service to get started
RUNS sleep 5
echo "# Waiting for jira service to be listening on port 8080"
docker exec -i go-jira-test tail -f screenlog.0 | grep -m 1 'jira started successfully' | sed 's/^/# /'
# wait for healthchecks to pass, curl will retry 900 times over 15 min waiting
RUNS curl -q -L --retry 900 --retry-delay 1 -f -s "http://localhost:8080/rest/api/2/serverInfo?doHealthCheck=1"
# login to jira as admin user
echo "admin" | RUNS $jira login
# create gojira user
RUNS $jira req -M POST /rest/api/2/user '{"name":"gojira","password":"gojira123","emailAddress":"gojira@example.com","displayName":"Go Jira"}'
# create mojira user (need secondary user for voting)
RUNS $jira req -M POST /rest/api/2/user '{"name":"mojira","password":"mojira123","emailAddress":"mojira@example.com","displayName":"Mo Jira"}'
# create SCRUM softwareproject
RUNS $jira req -M POST /rest/api/2/project '{"key":"SCRUM","name":"Scrum","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:gh-scrum-template","lead":"gojira"}'
# create KANBAN software project
RUNS $jira req -M POST /rest/api/2/project '{"key":"KANBAN","name":"Kanban","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:gh-kanban-template","lead":"gojira"}'
# create BAISC software project
RUNS $jira req -M POST /rest/api/2/project '{"key":"BASIC","name":"Basic","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:basic-software-development-template","lead":"gojira"}'
# create PROJECT business project
RUNS $jira req -M POST /rest/api/2/project '{"key":"PROJECT","name":"Project","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-project-management","lead":"gojira"}'
# create PROCESS business project
RUNS $jira req -M POST /rest/api/2/project '{"key":"PROCESS","name":"Process","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-process-management","lead":"gojira"}'
# create TASK business project
RUNS $jira req -M POST /rest/api/2/project '{"key":"TASK","name":"Task","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-task-management","lead":"gojira"}'
RUNS $jira logout
# export new templates so we are always using whatever is latest
# and not whatever is in the test-runners homedir
RUNS $jira export-templates -d .jira.d/templates
Executable
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira=../jira
PLAN 7
###############################################################################
## Verify logout works, we expect when we call the session api
## that we will get a 401 and prompt user for password
################################################################################
RUNS $jira logout
NRUNS $jira req /rest/auth/1/session </dev/null
ODIFF <<EOF
Jira Password [gojira]:
EOF
###############################################################################
## Verify login works (password read from stdin) and verify that the
## sesion api no longer prompts
###############################################################################
echo "gojira123" | RUNS $jira login
RUNS $jira req /rest/auth/1/session </dev/null
GREP '"name": "gojira"'
GREP '"self": "http://localhost:8080/rest/api/latest/user?username=gojira"'
Executable
+521
View File
@@ -0,0 +1,521 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project BASIC"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 86
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## List all issues, using the table template
###############################################################################
RUNS $jira ls --template table
DIFF <<EOF
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
| Issue | Summary | Priority | Status | Age | Reporter | Assignee |
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
| $(printf %-14s $issue) | summary | Medium | To Do | a minute | gojira | gojira |
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
EOF
###############################################################################
## Try to close the issue, bug Basic projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: To Do, In Progress, In Review, Done
EOF
###############################################################################
## put the issue into Done state, resolving it.
###############################################################################
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state
###############################################################################
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## set it back to "To Do"
###############################################################################
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set issue to "In Review" state
###############################################################################
RUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it back to "To Do"
###############################################################################
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "In Progress"
###############################################################################
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project BASIC"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 8
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Add a worklog to an issue
###############################################################################
RUNS $jira add worklog $issue --comment "work is hard" --time-spent "1h 12m" --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify worklog got added to issue
###############################################################################
RUNS $jira worklog $issue
DIFF <<EOF
- # gojira, a minute ago
comment: work is hard
timeSpent: 1h 12m
EOF
Executable
+508
View File
@@ -0,0 +1,508 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project SCRUM"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 84
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Try to close the issue, bug Basic projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: To Do, In Progress, Done
EOF
###############################################################################
## put the issue into Done state, resolving it.
###############################################################################
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state
###############################################################################
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## set it back to "To Do"
###############################################################################
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set issue to "In Review" state, which is an invalid state for SCRUM
###############################################################################
NRUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'review', Available: To Do, In Progress, Done
EOF
###############################################################################
## Set it back to "To Do"
###############################################################################
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "In Progress"
###############################################################################
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
Executable
+517
View File
@@ -0,0 +1,517 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project KANBAN"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 86
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Try to close the issue, bug Basic projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: Backlog, Selected for Development, In Progress, Done
EOF
###############################################################################
## put the issue into Done state, resolving it.
###############################################################################
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Backlog]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Backlog]
depends: $dup[Done]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Backlog]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state
###############################################################################
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## set it to "To Do", which is not a valid state for KANBAN issues
###############################################################################
NRUNS $jira todo $blocker
DIFF <<EOF
ERROR Invalid Transition 'To Do', Available: Backlog, Selected for Development, In Progress, Done
EOF
###############################################################################
## set issue back to backlog state
###############################################################################
RUNS $jira backlog $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set issue to "In Review" state, which is an invalid state for KANBAN
###############################################################################
NRUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'review', Available: Backlog, Selected for Development, In Progress, Done
EOF
###############################################################################
## Set it back to "Backlog"
###############################################################################
RUNS $jira backlog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "In Progress"
###############################################################################
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Backlog
summary: summary
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Backlog]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Backlog]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Backlog]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: mojira
reporter: gojira
blockers:
depends: $issue[Backlog]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Backlog]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
Executable
+520
View File
@@ -0,0 +1,520 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project PROJECT"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 84
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Try to close the issue, bug Basic projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: Start Progress, Done
EOF
###############################################################################
## put the issue into Done state, resolving it.
###############################################################################
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state, which is an invalid state for PROJECT
###############################################################################
NRUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'In Progress', Available: Start Progress, Done
EOF
###############################################################################
## Set issue to "In Review" state, which is an invalid state for PROJECT
###############################################################################
NRUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'review', Available: Start Progress, Done
EOF
###############################################################################
## Set it to "Start Progress" and verify that assignee is set to mojira
###############################################################################
RUNS $jira start $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: In Progress
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
description: |
blocks
EOF
###############################################################################
## Set it back to "Stop Progress"
###############################################################################
RUNS $jira stop $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
Executable
+513
View File
@@ -0,0 +1,513 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project PROCESS"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 84
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira start $issue; done) | sed 's/^/# CLEANUP: /g'
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira stop $issue; done) | sed 's/^/# CLEANUP: /g'
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Try to close the issue, but PROCESS projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: Start Progress
EOF
###############################################################################
## put the issue into Start Progress state, then Stop Progress state
## which will resolve the issue
###############################################################################
RUNS $jira start $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira stop $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved. For PROCESSS projects it has to go through
## 2 steps to resolve, one is "Start Progress" then resolved with "Stop
## Progress", so we see 3 updates in total
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Cancelled]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Open]
depends: $dup[Cancelled]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Open]
depends: $dup[Cancelled]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Open]
depends: $dup[Cancelled]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state, which is an invalid state for PROCESS
###############################################################################
NRUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'In Progress', Available: Start Progress
EOF
###############################################################################
## Set issue to "In Review" state, which is an invalid state for PROCESS
###############################################################################
NRUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'review', Available: Start Progress
EOF
###############################################################################
## Set it to "Start Progress"
###############################################################################
RUNS $jira start $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it back to "Stop Progress"
###############################################################################
RUNS $jira stop $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira reopen $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Open]
depends: $dup[Cancelled]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: Open
summary: summary
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Open]
depends: $dup[Cancelled]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Open]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Open]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Open]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[Open]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[Open]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
Executable
+504
View File
@@ -0,0 +1,504 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project TASK"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 82
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## View the issue we just created
###############################################################################
RUNS $jira view $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## List all issues, should be just the one we created
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Try to close the issue, but TASK projects do not allow that state
###############################################################################
NRUNS $jira close $issue
EDIFF <<EOF
ERROR Invalid Transition 'close', Available: Done
EOF
###############################################################################
## put the issue into Done state, which will resolve the issue
###############################################################################
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
###############################################################################
## Verify there are no unresolved issues
###############################################################################
RUNS $jira ls
DIFF <<EOF
EOF
###############################################################################
## Setup 2 more issues so we can test duping
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
EOF
###############################################################################
## Mark issue as duplicate, expect both issues to be updated and when viewing
## the main issue there should be a "depends" line showing the dup'd issue, and
## that issue should be resolved. For TASKS projects it has to go through
## 2 steps to resolve, one is "Start Progress" then resolved with "Stop
## Progress", so we see 3 updates in total
###############################################################################
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## We should see only one unresolved issue, the Dup should be resolved
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## Setup for testing blocking issues
###############################################################################
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Set blocker and verify it shows up when viewing the main issue
###############################################################################
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Both issues are unresolved now
###############################################################################
RUNS $jira ls
DIFF <<EOF
$(printf %-12s $issue:) summary
$(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
###############################################################################
jira="$jira --user mojira"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
###############################################################################
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 1
description: |
description
EOF
###############################################################################
## downvote the main issue, verify the vote count goes back to 0
###############################################################################
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[To Do]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
EOF
###############################################################################
## set issue to In Progress state, which is an invalid state for TASK
###############################################################################
NRUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'In Progress', Available: Done
EOF
###############################################################################
## Set issue to "In Review" state, which is an invalid state for TASK
###############################################################################
NRUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
ERROR Invalid Transition 'review', Available: Done
EOF
###############################################################################
## Set it to "Start Progress", which is an invalid state for TASK
###############################################################################
NRUNS $jira start $blocker
DIFF <<EOF
ERROR Invalid Transition 'start', Available: Done
EOF
###############################################################################
## Set it back to "Stop Progress", which is an invalid state for TASK
###############################################################################
NRUNS $jira stop $blocker
DIFF <<EOF
ERROR Invalid Transition 'stop', Available: Done
EOF
###############################################################################
## Set it to "Done"
###############################################################################
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
###############################################################################
## Verify issue is now in Done state (the "blocker" issue is now Done)
###############################################################################
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
EOF
###############################################################################
## Verify we can add a comment
###############################################################################
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
EOF
RUNS $jira $issue
DIFF <<EOF
issue: $issue
created: a minute ago
status: To Do
summary: summary
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers: $blocker[Done]
depends: $dup[Done]
priority: Medium
votes: 0
description: |
description
comments:
- | # mojira, a minute ago
Yo, Comment
EOF
###############################################################################
## Verify we can add labels to an issue
###############################################################################
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: another-label, test-label
description: |
blocks
EOF
###############################################################################
## Verify we can remove a label
###############################################################################
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: test-label
description: |
blocks
EOF
###############################################################################
## Verify we can replace the labels with a new set
###############################################################################
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: mojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## Verify we can give the issue back go "gojira" user
###############################################################################
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
EOF
RUNS $jira $blocker
DIFF <<EOF
issue: $blocker
created: a minute ago
status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: gojira
reporter: gojira
blockers:
depends: $issue[To Do]
priority: Medium
votes: 0
labels: better-label, more-label
description: |
blocks
EOF
+20
View File
@@ -0,0 +1,20 @@
FROM alpine:latest
RUN apk --update add openjdk8-jre curl screen && \
curl -s -L https://marketplace.atlassian.com/download/plugins/atlassian-plugin-sdk-tgz | tar xzf - && \
ln -s /atlassian* /atlassian
ENV PATH=/bin:/usr/bin:/atlassian/bin
# Copy in the serivce and also the root .m2 settings to force cache everything.
# We also copy in /root/.java settings to prevent the dumb spam prompt from
# the atlas-run command:
# Would you like to subscribe to the Atlassian developer mailing list? (Y/y/N/n) Y: :
COPY dockerroot /
WORKDIR /jiratestservice
EXPOSE 8080
# we wrap the command with screen so that the dumb atlas-run has a tty to watch. Without screen
# there is no tty so atlas-run will immediately read an EOF (aka CTRL-D) and interpret that to
# mean we want the service to begin the "graceful shutdown" and exit
CMD ["screen", "-DmL", "atlas-run", "--http-port", "8080", "--context-path", "ROOT", "--server", "localhost"]
+37
View File
@@ -0,0 +1,37 @@
## Tests
The test are written using the `osht` bash testing framework. Please read the [documentation](https://github.com/coryb/osht/blob/master/README.md) for `osht`.
## Running Test:
From the top level of the project you can run:
```
# this creates a local "jira" binary
make
# this runs the integration tests in the "t" directory
prove
```
### Running individual tests
To run a specific test you can run it directly like:
```
./100basic.t
```
There is a useful `-v` option to make the test more verbose and an `-a` option to casue the test to abort after the first failure.
The tests all require the jira service to be running from the docker container, so you will have to manually run the setup script:
```
./000setup.t
```
After than you can run the other tests over and over. The jira service is just a test instance started for local development. It comes with
a temporary license (I think it is 8 hours) so you will have to run the `./000setup.t` script at least once daily.
## API Documentation:
https://docs.atlassian.com/jira/REST/cloud/
https://docs.atlassian.com/jira-software/REST/cloud
## projectTempalteKey missing documentation
https://answers.atlassian.com/questions/36176301/jira-api-7.1.0-create-project
+6
View File
@@ -0,0 +1,6 @@
To avoid future confusion, we recommend that you include a license with your plugin.
This file is simply a reminder.
For a template license you can have a look at: http://www.opensource.org/licenses/
Atlassian releases most of its modules under the Apache2 license: http://opensource.org/licenses/Apache-2.0
+13
View File
@@ -0,0 +1,13 @@
You have successfully created an Atlassian Plugin!
Here are the SDK commands you'll use immediately:
* atlas-run -- installs this plugin into the product and starts it on localhost
* atlas-debug -- same as atlas-run, but allows a debugger to attach at port 5005
* atlas-cli -- after atlas-run or atlas-debug, opens a Maven command line window:
- 'pi' reinstalls the plugin into the running product instance
* atlas-help -- prints description for all commands in the SDK
Full documentation is always available at:
https://developer.atlassian.com/display/DOCS/Introduction+to+the+Atlassian+Plugin+SDK
+185
View File
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.netflixskunkworks</groupId>
<artifactId>jiratestservice</artifactId>
<version>1.0</version>
<organization>
<name>Example Company</name>
<url>http://www.example.com/</url>
</organization>
<name>jiratestservice</name>
<description>This is the com.netflixskunkworks:jiratestservice plugin for Atlassian JIRA.</description>
<packaging>atlassian-plugin</packaging>
<dependencies>
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
<!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. -->
<!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x -->
<!--
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-core</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-runtime</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
<!-- WIRED TEST RUNNER DEPENDENCIES -->
<dependency>
<groupId>com.atlassian.plugins</groupId>
<artifactId>atlassian-plugins-osgi-testrunner</artifactId>
<version>${plugin.testrunner.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.2-atlassian-1</version>
</dependency>
<!-- Uncomment to use TestKit in your project. Details at https://bitbucket.org/atlassian/jira-testkit -->
<!-- You can read more about TestKit at https://developer.atlassian.com/display/JIRADEV/Plugin+Tutorial+-+Smarter+integration+testing+with+TestKit -->
<!--
<dependency>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-client</artifactId>
<version>${testkit.version}</version>
<scope>test</scope>
</dependency>
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-jira-plugin</artifactId>
<version>${amps.version}</version>
<extensions>true</extensions>
<configuration>
<applications>
<application>
<applicationKey>jira-software</applicationKey>
<version>${jira.version}</version>
</application>
</applications>
<productVersion>${jira.version}</productVersion>
<productDataVersion>${jira.version}</productDataVersion>
<!-- Uncomment to install TestKit backdoor in JIRA. -->
<!--
<pluginArtifacts>
<pluginArtifact>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-plugin</artifactId>
<version>${testkit.version}</version>
</pluginArtifact>
</pluginArtifacts>
-->
<enableQuickReload>true</enableQuickReload>
<enableFastdev>false</enableFastdev>
<!-- See here for an explanation of default instructions: -->
<!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
<instructions>
<Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
<!-- Add package to export here -->
<Export-Package>
com.netflixskunkworks.api,
</Export-Package>
<!-- Add package import here -->
<Import-Package>
org.springframework.osgi.*;resolution:="optional",
org.eclipse.gemini.blueprint.*;resolution:="optional",
*
</Import-Package>
<!-- Ensure plugin is spring powered -->
<Spring-Context>*</Spring-Context>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<executions>
<execution>
<goals>
<goal>atlassian-spring-scanner</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
<configuration>
<scannedDependencies>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-external-jar</artifactId>
</dependency>
</scannedDependencies>
<verbose>false</verbose>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<jira.version>7.2.0</jira.version>
<amps.version>6.2.6</amps.version>
<plugin.testrunner.version>1.2.3</plugin.testrunner.version>
<atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
<!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
<atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
<!-- TestKit version 6.x for JIRA 6.x -->
<testkit.version>6.3.11</testkit.version>
</properties>
</project>
@@ -0,0 +1,7 @@
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
</plugin-info>
</atlassian-plugin>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map MAP_XML_VERSION="1.0">
<entry key="sdk-email-subscribe" value="true"/>
</map>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map MAP_XML_VERSION="1.0">
<entry key="last_update_check" value="2016-08-29"/>
<entry key="sdk-pom-update-check-6.2.6-cbc3c672c37f65828d50132ed303cf7a" value="true"/>
</map>
+77
View File
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<profiles>
<!-- Default profile containing Atlassian servers -->
<profile>
<id>defaultProfile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>atlassian-public</id>
<url>https://maven.atlassian.com/repository/public</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
<repository>
<id>atlassian-plugin-sdk</id>
<url>file://${env.ATLAS_HOME}/repository</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>atlassian-public</id>
<url>https://maven.atlassian.com/repository/public</url>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>atlassian-plugin-sdk</id>
<url>file://${env.ATLAS_HOME}/repository</url>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
<properties>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</properties>
</profile>
</profiles>
</settings>