loljira 0.0.0
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/bin/loljira +60 -0
- data/lib/component.rb +10 -0
- data/lib/jira.rb +82 -0
- data/lib/message_parser.rb +74 -0
- data/lib/sprint.rb +22 -0
- metadata +52 -0
data/bin/loljira
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'trollop'
|
4
|
+
require 'httparty'
|
5
|
+
require_relative '../lib/sprint.rb'
|
6
|
+
require_relative '../lib/message_parser.rb'
|
7
|
+
require_relative '../lib/component.rb'
|
8
|
+
|
9
|
+
options = Trollop::options do
|
10
|
+
opt :username, "username that you login with on jira", :type => String
|
11
|
+
opt :password, "login password (not stored)", :type => String
|
12
|
+
opt :url, "jira api endpoint", :type => String
|
13
|
+
opt :projectname, "project name (key, not id!)", :type => String
|
14
|
+
opt :projectid, "project id"
|
15
|
+
opt :file, "file to the git commit message", :type => String
|
16
|
+
opt :sprint, "sprint id", :type => String
|
17
|
+
end
|
18
|
+
|
19
|
+
JIRA_URL = options[ :url ].to_s
|
20
|
+
JIRA_USERNAME = options[ :username ].to_s
|
21
|
+
JIRA_PASSWORD = options[ :password ].to_s
|
22
|
+
JIRA_PROJECT_NAME = options[ :projectname ].to_s
|
23
|
+
JIRA_SPRINT_ID = options[:sprint].to_i
|
24
|
+
FPATH = options[:file].to_s
|
25
|
+
|
26
|
+
unless FPATH.length > 0
|
27
|
+
puts "File required!"
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
require_relative '../lib/jira.rb'
|
32
|
+
|
33
|
+
file = File.open FPATH, "r"
|
34
|
+
contents = file.read
|
35
|
+
file.close
|
36
|
+
if MessageParser.already_in_jira?( contents , /#?CNVS\-/ )
|
37
|
+
puts "commit message already appears to have a jira ID. skipping."
|
38
|
+
exit 0
|
39
|
+
end
|
40
|
+
message = MessageParser.parse( contents )
|
41
|
+
puts message.fields
|
42
|
+
if !message.components?
|
43
|
+
puts "Components are required!"
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
jira = Jira.new( JIRA_USERNAME, JIRA_PASSWORD, JIRA_URL )
|
47
|
+
issue = jira.create_issue({'summary'=> message.summary,
|
48
|
+
'project'=> {'key' => JIRA_PROJECT_NAME},
|
49
|
+
'issuetype' => {'id'=> "7" },
|
50
|
+
'assignee' => { 'name' => JIRA_USERNAME },
|
51
|
+
'description' => message.description,
|
52
|
+
'components' => message.components
|
53
|
+
})
|
54
|
+
jira.add_issue_to_sprint issue.key, jira.first_open_sprint( JIRA_SPRINT_ID ).id
|
55
|
+
|
56
|
+
file = File.open FPATH, "w+"
|
57
|
+
file.write message.without_fields_s( "fixes ##{issue.key}" )
|
58
|
+
file.close
|
59
|
+
puts "Issue ##{issue.key} Created!"
|
60
|
+
exit 0
|
data/lib/component.rb
ADDED
data/lib/jira.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
class Issue
|
2
|
+
attr_reader :id, :key, :self_url
|
3
|
+
|
4
|
+
def initialize( args )
|
5
|
+
@self_url = args.fetch( 'self' )
|
6
|
+
@id = args.fetch( 'id' )
|
7
|
+
@key = args.fetch( 'key' )
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
require 'json'
|
12
|
+
class Jira
|
13
|
+
include HTTParty
|
14
|
+
|
15
|
+
base_uri JIRA_URL
|
16
|
+
|
17
|
+
def initialize( username, password, endpoint_url )
|
18
|
+
@url = endpoint_url
|
19
|
+
@username = username
|
20
|
+
@password = password
|
21
|
+
end
|
22
|
+
|
23
|
+
def components
|
24
|
+
response = self.class.get "/project/CNVS/components", auth
|
25
|
+
response.map do |item|
|
26
|
+
Component.new item['id'], item['name']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def sprints( rapid_board_id )
|
31
|
+
response = self.class.get "/greenhopper/1.0/sprints/#{rapid_board_id}", auth
|
32
|
+
response['sprints'].map{ |sprint|
|
33
|
+
spr = {
|
34
|
+
'id' => sprint['id'],
|
35
|
+
'name' => sprint['name'],
|
36
|
+
'closed' => sprint[ 'closed' ],
|
37
|
+
'end_date' => sprint[ 'name' ].split( " " ).last.to_s
|
38
|
+
}
|
39
|
+
Sprint.new spr
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def first_open_sprint( rapid_board_id )
|
44
|
+
sprints = sprints(rapid_board_id).sort_by(&:end_date)
|
45
|
+
sprint = sprints.detect{ |sprint| sprint.open? }
|
46
|
+
if !sprint
|
47
|
+
sprint = sprints.last
|
48
|
+
end
|
49
|
+
sprint
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_issue_to_sprint( issue_id, sprint_id )
|
53
|
+
options = auth.merge({:body => JSON.dump({ 'issueKeys' => [ issue_id ] }) }).
|
54
|
+
merge({:headers => { "Content-Type" => "application/json",
|
55
|
+
"Accept" => "application/json"
|
56
|
+
}})
|
57
|
+
self.class.put "/greenhopper/1.0/sprint/#{sprint_id}/issues/add", options
|
58
|
+
end
|
59
|
+
|
60
|
+
def projects
|
61
|
+
self.class.get "/api/2/project", auth
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_project_by_key( project_key )
|
65
|
+
projects = self.class.get "/api/2/project", auth
|
66
|
+
projects.detect { | project | project['key'] == project_key }
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_issue( issue_params )
|
70
|
+
body = { :body => JSON.dump({'fields'=> issue_params }) }
|
71
|
+
body = body.merge({:headers => {"Content-Type" => "application/json", "Accept" => "application/json"}})
|
72
|
+
Issue.new( self.class.post "/api/2/issue", body.merge(auth) )
|
73
|
+
end
|
74
|
+
|
75
|
+
def auth
|
76
|
+
{
|
77
|
+
:basic_auth =>
|
78
|
+
{:username => @username, :password => @password}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Message
|
2
|
+
|
3
|
+
attr_reader :summary,:description,:fields
|
4
|
+
|
5
|
+
def initialize( summary, description, fields)
|
6
|
+
@summary = summary.to_s
|
7
|
+
@description = description.to_s
|
8
|
+
@fields = fields
|
9
|
+
end
|
10
|
+
|
11
|
+
def components
|
12
|
+
fields['components']
|
13
|
+
end
|
14
|
+
|
15
|
+
def components?
|
16
|
+
!!components
|
17
|
+
end
|
18
|
+
|
19
|
+
def without_fields_s( commit_msg = "" )
|
20
|
+
@summary + "\n" + @description + "\n" + commit_msg.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class MessageParser
|
26
|
+
|
27
|
+
def self.commit_summary_and_body( message )
|
28
|
+
messages = message.split("\n")
|
29
|
+
return messages.first.to_s, (messages[1..-1]).join("\n").split(/jira:/).first.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.parse( message )
|
33
|
+
summary, body = commit_summary_and_body( message )
|
34
|
+
fields = split_keys (message.split /jira:/).last.to_s.split( /Change-Id/ ).first.to_s.strip
|
35
|
+
Message.new( summary, body, fields )
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.listify_components( value )
|
39
|
+
return [] if value.length == 0
|
40
|
+
value.split(',').map do | component_name |
|
41
|
+
{:name => component_name.to_s}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.split_keys( message )
|
46
|
+
message.split( "\n" ).inject({}) do | memo, line |
|
47
|
+
key, value = line.split( ":" )
|
48
|
+
key = key.to_s.strip.downcase; value = value.to_s.strip
|
49
|
+
if key == 'components'
|
50
|
+
value = listify_components value
|
51
|
+
end
|
52
|
+
memo[key] = value
|
53
|
+
memo
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.already_in_jira?( msg, msg_regex )
|
58
|
+
!!msg.match(msg_regex)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.message
|
62
|
+
<<-eos
|
63
|
+
this is the example commit message
|
64
|
+
blah blah blah blah blah
|
65
|
+
|
66
|
+
jira:
|
67
|
+
components#Assignments
|
68
|
+
sprint#15
|
69
|
+
|
70
|
+
Change-Id: 982374ujalskdfj
|
71
|
+
eos
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/sprint.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
class Sprint
|
4
|
+
|
5
|
+
attr_reader :id, :name, :closed, :end_date
|
6
|
+
|
7
|
+
def initialize( args )
|
8
|
+
@id = args.fetch( 'id', '' )
|
9
|
+
@name = args.fetch( 'name', '' )
|
10
|
+
@closed = !!args.fetch( 'closed', false )
|
11
|
+
@end_date = Date.parse args.fetch( 'end_date' )
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"Sprint id: #{id} name: #{name}, closed: #{closed?}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def closed?; !!closed; end
|
19
|
+
def open?; !closed?; end
|
20
|
+
def over?; open?; end
|
21
|
+
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: loljira
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stanley Stuart
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-16 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: command line utility for use with git to automatically create jira tickets
|
15
|
+
email:
|
16
|
+
- dstanley.stuart@gmail.com
|
17
|
+
executables:
|
18
|
+
- loljira
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/jira.rb
|
23
|
+
- lib/sprint.rb
|
24
|
+
- lib/message_parser.rb
|
25
|
+
- lib/component.rb
|
26
|
+
- bin/loljira
|
27
|
+
homepage:
|
28
|
+
licenses: []
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 1.8.23
|
48
|
+
signing_key:
|
49
|
+
specification_version: 3
|
50
|
+
summary: loljira
|
51
|
+
test_files: []
|
52
|
+
has_rdoc:
|