geera 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +23 -0
- data/ChangeLog +43 -0
- data/Manifest.txt +30 -0
- data/README.rdoc +189 -0
- data/Rakefile +15 -0
- data/bin/geera +118 -0
- data/lib/geera.rb +23 -0
- data/lib/geera/client.rb +63 -0
- data/lib/geera/commands/assign.rb +13 -0
- data/lib/geera/commands/command.rb +19 -0
- data/lib/geera/commands/estimate.rb +13 -0
- data/lib/geera/commands/filters.rb +16 -0
- data/lib/geera/commands/fix.rb +15 -0
- data/lib/geera/commands/list.rb +29 -0
- data/lib/geera/commands/show.rb +13 -0
- data/lib/geera/commands/start.rb +17 -0
- data/lib/geera/commands/take.rb +13 -0
- data/lib/geera/executable.rb +10 -0
- data/lib/geera/ticket.rb +116 -0
- data/lib/template.txt +12 -0
- data/test/commands/test_assign.rb +27 -0
- data/test/commands/test_estimate.rb +27 -0
- data/test/commands/test_filters.rb +34 -0
- data/test/commands/test_fix.rb +74 -0
- data/test/commands/test_list.rb +90 -0
- data/test/commands/test_show.rb +34 -0
- data/test/commands/test_start.rb +28 -0
- data/test/commands/test_take.rb +31 -0
- data/test/test_client.rb +214 -0
- data/test/test_ticket.rb +19 -0
- metadata +170 -0
data/lib/geera/client.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Geera
|
2
|
+
class Client
|
3
|
+
attr_reader :username
|
4
|
+
attr_reader :ctx
|
5
|
+
|
6
|
+
def initialize url
|
7
|
+
@ctx = Jira4R::JiraTool.new 2, url
|
8
|
+
|
9
|
+
# Make jira4r quiet
|
10
|
+
@ctx.logger = Logger.new nil
|
11
|
+
@username = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
###
|
15
|
+
# Login with +user+ and +password+
|
16
|
+
def login user, password
|
17
|
+
@username = user
|
18
|
+
@ctx.login user, password
|
19
|
+
end
|
20
|
+
|
21
|
+
###
|
22
|
+
# Get the ticket with +number+. Returns a Geera::Ticket object.
|
23
|
+
#
|
24
|
+
# client.ticket 'BZ-123' # => #<Geera::Ticket>
|
25
|
+
def ticket number
|
26
|
+
Geera::Ticket.new self, number
|
27
|
+
end
|
28
|
+
|
29
|
+
###
|
30
|
+
# Create a ticket using +params+. +params+ should be a hash, and *must*
|
31
|
+
# have a +project+, +summary+, and +description+ field.
|
32
|
+
#
|
33
|
+
# For example:
|
34
|
+
#
|
35
|
+
# client.create_ticket :project => 'AB',
|
36
|
+
# :summary => 'foo',
|
37
|
+
# :description => 'bar'
|
38
|
+
#
|
39
|
+
def create_ticket params
|
40
|
+
[:project, :summary].each do |param|
|
41
|
+
raise(ArgumentError, "#{param} required") unless params[param]
|
42
|
+
end
|
43
|
+
|
44
|
+
issue = Jira4R::V2::RemoteIssue.new
|
45
|
+
issue.project = params[:project]
|
46
|
+
issue.summary = params[:summary]
|
47
|
+
issue.description = params[:description]
|
48
|
+
issue.assignee = params[:assignee] || @username
|
49
|
+
issue.type = '1' #FIXME: wtf is this for?
|
50
|
+
issue.priority = '5'
|
51
|
+
issue = @ctx.createIssue issue
|
52
|
+
ticket issue.key
|
53
|
+
end
|
54
|
+
|
55
|
+
def filters
|
56
|
+
@ctx.getSavedFilters
|
57
|
+
end
|
58
|
+
|
59
|
+
def list filter
|
60
|
+
@ctx.getIssuesFromFilter filter
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Geera
|
2
|
+
module Commands
|
3
|
+
class Command
|
4
|
+
attr_reader :geera, :number, :config, :argv
|
5
|
+
|
6
|
+
def initialize config, number, geera, argv
|
7
|
+
@argv = argv
|
8
|
+
@config = config
|
9
|
+
@number = number
|
10
|
+
@geera = geera
|
11
|
+
@ticket = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def ticket
|
15
|
+
@ticket ||= geera.ticket number
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Geera
|
2
|
+
module Commands
|
3
|
+
class Filters < Geera::Commands::Command
|
4
|
+
def self.handle? command
|
5
|
+
'filters' == command
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute!
|
9
|
+
puts "# Your filters"
|
10
|
+
geera.filters.each do |filter|
|
11
|
+
puts " * #{filter.name.inspect}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Geera
|
2
|
+
module Commands
|
3
|
+
class Fix < Geera::Commands::Command
|
4
|
+
def self.handle? command
|
5
|
+
'fix' == command
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute!
|
9
|
+
ticket.start! if ticket.startable?
|
10
|
+
ticket.fix! if ticket.fixable?
|
11
|
+
ticket.assign_to config['qa'] if config['qa']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Geera
|
2
|
+
module Commands
|
3
|
+
class List < Geera::Commands::Command
|
4
|
+
def self.handle? command
|
5
|
+
'list' == command
|
6
|
+
end
|
7
|
+
|
8
|
+
def number
|
9
|
+
## Ensure that +number+ is actually a number
|
10
|
+
begin
|
11
|
+
@number = Integer(super)
|
12
|
+
rescue ArgumentError
|
13
|
+
@number = geera.filters.find { |f| f.name == super }.id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute!
|
18
|
+
tickets = geera.list number
|
19
|
+
if tickets.empty?
|
20
|
+
puts "No Tickets found"
|
21
|
+
else
|
22
|
+
tickets.first(10).each do |ticket|
|
23
|
+
puts "#{ticket.key.ljust(8)} #{ticket.summary}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Geera
|
2
|
+
module Commands
|
3
|
+
class Start < Geera::Commands::Command
|
4
|
+
def self.handle? command
|
5
|
+
'start' == command
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute!
|
9
|
+
unless ticket.startable?
|
10
|
+
abort "Ticket #{@number} is not startable. Available actions:\n" \
|
11
|
+
" - #{ticket.available_actions.map { |x| x.name + "\n" }.join(" - ")}"
|
12
|
+
end
|
13
|
+
ticket.start!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/geera/ticket.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Geera
|
4
|
+
class Ticket
|
5
|
+
attr_reader :number
|
6
|
+
|
7
|
+
###
|
8
|
+
# Create a new Ticket using Geera::Client +client+ and +ticket_number+.
|
9
|
+
# This constructor should not be called directly, use Geera::Client#ticket
|
10
|
+
# instead.
|
11
|
+
def initialize client, ticket_number
|
12
|
+
@client = client
|
13
|
+
@ctx = client.ctx
|
14
|
+
@number = ticket_number
|
15
|
+
@issue = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
###
|
19
|
+
# Add a comment to this ticket with +text+.
|
20
|
+
def comment text
|
21
|
+
@ctx.addComment @number, Jira4R::V2::RemoteComment.new(nil, text)
|
22
|
+
end
|
23
|
+
|
24
|
+
###
|
25
|
+
# Returns a list of the actions that can be performed on this ticket.
|
26
|
+
def available_actions
|
27
|
+
@ctx.getAvailableActions @number
|
28
|
+
end
|
29
|
+
|
30
|
+
def startable?
|
31
|
+
available_actions.map { |a| a.name }.include?('Start')
|
32
|
+
end
|
33
|
+
|
34
|
+
###
|
35
|
+
# Start this ticket.
|
36
|
+
def start!
|
37
|
+
action = available_actions.find { |x| x.name == 'Start' }
|
38
|
+
@ctx.progressWorkflowAction(@number, action.id, passthrough_attributes)
|
39
|
+
end
|
40
|
+
|
41
|
+
###
|
42
|
+
# Is this ticket in a Fix able state?
|
43
|
+
def fixable?
|
44
|
+
available_actions.map { |a| a.name }.include?('Fix')
|
45
|
+
end
|
46
|
+
|
47
|
+
###
|
48
|
+
# Fix this ticket.
|
49
|
+
def fix!
|
50
|
+
action = available_actions.find { |x| x.name == 'Fix' }
|
51
|
+
@ctx.progressWorkflowAction(@number, action.id, passthrough_attributes + [
|
52
|
+
Jira4R::V2::RemoteFieldValue.new('customfield_10176', @client.username),
|
53
|
+
])
|
54
|
+
end
|
55
|
+
|
56
|
+
###
|
57
|
+
# Assign this ticket to +username+
|
58
|
+
def assign_to username
|
59
|
+
assign = Jira4R::V2::RemoteFieldValue.new('assignee', username)
|
60
|
+
@ctx.updateIssue(@number, [assign])
|
61
|
+
end
|
62
|
+
|
63
|
+
def estimate= amount
|
64
|
+
assign = Jira4R::V2::RemoteFieldValue.new('timetracking', amount)
|
65
|
+
@ctx.updateIssue(@number, [assign])
|
66
|
+
end
|
67
|
+
|
68
|
+
def description
|
69
|
+
issue.description
|
70
|
+
end
|
71
|
+
|
72
|
+
def inspect
|
73
|
+
template = ERB.new <<-eoinspect
|
74
|
+
## Reporter: <%= issue.reporter %>
|
75
|
+
## Assignee: <%= issue.assignee %>
|
76
|
+
## Priority: <%= issue.priority %>
|
77
|
+
## Fix Versions: <%= issue.fixVersions.map {|fv| fv.name}.join(', ') %>
|
78
|
+
|
79
|
+
## Summary
|
80
|
+
|
81
|
+
<%= issue.summary %>
|
82
|
+
|
83
|
+
## Description:
|
84
|
+
|
85
|
+
<%= issue.description %>
|
86
|
+
|
87
|
+
## Comments:
|
88
|
+
<% comments.each do |comment| %>
|
89
|
+
* <%= comment.author %>
|
90
|
+
<% comment.body.split(/[\r\n]+/).each do |line| %>
|
91
|
+
> <%= line %><% end %>
|
92
|
+
<% end %>
|
93
|
+
|
94
|
+
eoinspect
|
95
|
+
template.result binding
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def issue
|
100
|
+
@issue ||= @ctx.getIssue(@number)
|
101
|
+
end
|
102
|
+
|
103
|
+
def comments
|
104
|
+
@ctx.getComments @number
|
105
|
+
end
|
106
|
+
|
107
|
+
def passthrough_attributes
|
108
|
+
[
|
109
|
+
Jira4R::V2::RemoteFieldValue.new('assignee', @client.username),
|
110
|
+
Jira4R::V2::RemoteFieldValue.new('description', description),
|
111
|
+
Jira4R::V2::RemoteFieldValue.new('priority', issue.priority),
|
112
|
+
]
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
data/lib/template.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "geera"
|
3
|
+
require 'flexmock/test_unit'
|
4
|
+
|
5
|
+
module Geera
|
6
|
+
module Commands
|
7
|
+
class TestAssign < Test::Unit::TestCase
|
8
|
+
def test_handle?
|
9
|
+
assert Assign.handle?('assign')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_execute!
|
13
|
+
recorder = Object.new
|
14
|
+
flexmock(recorder) do |thing|
|
15
|
+
thing.should_receive(:assign_to).with('foo').once
|
16
|
+
end
|
17
|
+
|
18
|
+
Class.new(Geera::Commands::Assign) {
|
19
|
+
def initialize ticket, argv
|
20
|
+
super(nil, nil, nil, argv)
|
21
|
+
@ticket = ticket
|
22
|
+
end
|
23
|
+
}.new(recorder, ['foo']).execute!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "geera"
|
3
|
+
require 'flexmock/test_unit'
|
4
|
+
|
5
|
+
module Geera
|
6
|
+
module Commands
|
7
|
+
class TestEstimate < Test::Unit::TestCase
|
8
|
+
def test_handle?
|
9
|
+
assert Estimate.handle?('estimate')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_execute!
|
13
|
+
recorder = Object.new
|
14
|
+
flexmock(recorder) do |thing|
|
15
|
+
thing.should_receive(:estimate=).with('3h').once
|
16
|
+
end
|
17
|
+
|
18
|
+
Class.new(Geera::Commands::Estimate) {
|
19
|
+
def initialize ticket, argv
|
20
|
+
super(nil, nil, nil, argv)
|
21
|
+
@ticket = ticket
|
22
|
+
end
|
23
|
+
}.new(recorder, %w{3h}).execute!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "geera"
|
3
|
+
require 'flexmock/test_unit'
|
4
|
+
|
5
|
+
module Geera
|
6
|
+
module Commands
|
7
|
+
class TestFilters < Test::Unit::TestCase
|
8
|
+
def test_handle?
|
9
|
+
assert Filters.handle?('filters')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_execute!
|
13
|
+
filter = Struct.new(:name).new('hi')
|
14
|
+
|
15
|
+
recorder = Object.new
|
16
|
+
flexmock(recorder) do |thing|
|
17
|
+
thing.should_receive(:filters).once.and_return([filter])
|
18
|
+
end
|
19
|
+
|
20
|
+
cmd = fake_command.new(recorder)
|
21
|
+
flexmock(cmd).should_receive(:puts)
|
22
|
+
cmd.execute!
|
23
|
+
end
|
24
|
+
|
25
|
+
def fake_command
|
26
|
+
Class.new(Geera::Commands::Filters) {
|
27
|
+
def initialize geera
|
28
|
+
super(nil, nil, geera, nil)
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|