meta_project 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +178 -0
- data/MIT-LICENSE +21 -0
- data/README +64 -0
- data/Rakefile +166 -0
- data/TODO +9 -0
- data/doc/base_attrs.rdoc +2 -0
- data/lib/meta_project/project/base.rb +7 -0
- data/lib/meta_project/project/codehaus/codehaus_project_svn.rb +26 -0
- data/lib/meta_project/project/codehaus.rb +1 -0
- data/lib/meta_project/project/trac/trac_project.rb +26 -0
- data/lib/meta_project/project/trac.rb +1 -0
- data/lib/meta_project/project/xforge/ruby_forge.rb +46 -0
- data/lib/meta_project/project/xforge/session.rb +162 -0
- data/lib/meta_project/project/xforge/source_forge.rb +46 -0
- data/lib/meta_project/project/xforge/xfile.rb +45 -0
- data/lib/meta_project/project/xforge/xforge_base.rb +76 -0
- data/lib/meta_project/project/xforge.rb +5 -0
- data/lib/meta_project/project.rb +4 -0
- data/lib/meta_project/project_analyzer.rb +36 -0
- data/lib/meta_project/scm_web.rb +53 -0
- data/lib/meta_project/tracker/base.rb +18 -0
- data/lib/meta_project/tracker/digit_issues.rb +24 -0
- data/lib/meta_project/tracker/issue.rb +11 -0
- data/lib/meta_project/tracker/jira/jira_tracker.rb +68 -0
- data/lib/meta_project/tracker/jira.rb +1 -0
- data/lib/meta_project/tracker/trac/trac_tracker.rb +29 -0
- data/lib/meta_project/tracker/trac.rb +1 -0
- data/lib/meta_project/tracker/xforge/ruby_forge_tracker.rb +17 -0
- data/lib/meta_project/tracker/xforge/source_forge_tracker.rb +17 -0
- data/lib/meta_project/tracker/xforge/xforge_tracker.rb +83 -0
- data/lib/meta_project/tracker/xforge.rb +3 -0
- data/lib/meta_project/tracker.rb +6 -0
- data/lib/meta_project/version_parser.rb +48 -0
- data/lib/meta_project.rb +6 -0
- data/lib/rake/contrib/xforge/base.rb +42 -0
- data/lib/rake/contrib/xforge/news_publisher.rb +34 -0
- data/lib/rake/contrib/xforge/release.rb +78 -0
- data/lib/rake/contrib/xforge.rb +3 -0
- metadata +85 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module MetaProject
|
5
|
+
module Project
|
6
|
+
module XForge
|
7
|
+
|
8
|
+
# A Session object allows authenticated interaction with a Project, such as releasing files.
|
9
|
+
#
|
10
|
+
# A Session object can be obtained via Project.login
|
11
|
+
#
|
12
|
+
class Session
|
13
|
+
|
14
|
+
# Simple enumeration of processors. Used from Session.release
|
15
|
+
class Processor
|
16
|
+
I386 = 1000
|
17
|
+
IA64 = 6000
|
18
|
+
ALPHA = 7000
|
19
|
+
ANY = 8000
|
20
|
+
PPC = 2000
|
21
|
+
MIPS = 3000
|
22
|
+
SPARC = 4000
|
23
|
+
ULTRA_SPARC = 5000
|
24
|
+
OTHER_PLATFORM = 9999
|
25
|
+
end
|
26
|
+
|
27
|
+
BOUNDARY = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
|
28
|
+
|
29
|
+
def initialize(host, project, cookie) # :nodoc:
|
30
|
+
@host = host
|
31
|
+
@project = project
|
32
|
+
@headers = { "Cookie" => cookie }
|
33
|
+
end
|
34
|
+
|
35
|
+
# The package_id of our project
|
36
|
+
def package_id
|
37
|
+
unless(@package_id)
|
38
|
+
release_uri = "http://#{@host}/frs/admin/?group_id=#{@project.group_id}"
|
39
|
+
release_data = open(release_uri, @headers) { |data| data.read }
|
40
|
+
@package_id = release_data[/[?&]package_id=(\d+)/, 1]
|
41
|
+
raise "Couldn't get package_id" unless @package_id
|
42
|
+
end
|
43
|
+
@package_id
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a new release containing the files specified by +filenames+ (Array) and named +release_name+.
|
47
|
+
# Optional parameters are +processor+ (which should be one of the Processor constants), +release_notes+,
|
48
|
+
# +release_changes+ and +preformatted+ which will appear on the releas page of the associated project.
|
49
|
+
#
|
50
|
+
def release(release_name, filenames, release_notes="", release_changes="", preformatted=true, processor=Processor::ANY)
|
51
|
+
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
|
52
|
+
release_id = nil
|
53
|
+
|
54
|
+
puts "About to release '#{release_name}'"
|
55
|
+
puts "Files:"
|
56
|
+
puts " " + filenames.join("\n ")
|
57
|
+
puts "\nRelease Notes:\n"
|
58
|
+
puts release_notes
|
59
|
+
puts "\nRelease Changes:\n"
|
60
|
+
puts release_changes
|
61
|
+
puts "\nRelease Settings:\n"
|
62
|
+
puts "Preformatted: #{preformatted}"
|
63
|
+
puts "Processor: #{processor}"
|
64
|
+
puts "\nStarting release..."
|
65
|
+
|
66
|
+
xfiles = filenames.collect{|filename| XFile.new(filename)}
|
67
|
+
xfiles.each_with_index do |xfile, i|
|
68
|
+
first_file = i==0
|
69
|
+
puts "Releasing #{xfile.basename}..."
|
70
|
+
|
71
|
+
release_response = Net::HTTP.start(@host, 80) do |http|
|
72
|
+
query_hash = if first_file then
|
73
|
+
{
|
74
|
+
"group_id" => @project.group_id,
|
75
|
+
"package_id" => package_id,
|
76
|
+
"type_id" => xfile.bin_type_id,
|
77
|
+
"processor_id" => processor,
|
78
|
+
|
79
|
+
"release_name" => release_name,
|
80
|
+
"release_date" => release_date,
|
81
|
+
"release_notes" => release_notes,
|
82
|
+
"release_changes" => release_changes,
|
83
|
+
"preformatted" => preformatted ? "1" : "0",
|
84
|
+
"submit" => "1"
|
85
|
+
}
|
86
|
+
else
|
87
|
+
{
|
88
|
+
"group_id" => @project.group_id,
|
89
|
+
"package_id" => package_id,
|
90
|
+
"type_id" => xfile.bin_type_id,
|
91
|
+
"processor_id" => processor,
|
92
|
+
|
93
|
+
"step2" => "1",
|
94
|
+
"release_id" => release_id,
|
95
|
+
"submit" => "Add This File"
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
query = query(query_hash)
|
100
|
+
|
101
|
+
data = [
|
102
|
+
"--" + BOUNDARY,
|
103
|
+
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{xfile.basename}\"",
|
104
|
+
"Content-Type: application/octet-stream",
|
105
|
+
"Content-Transfer-Encoding: binary",
|
106
|
+
"", xfile.data, ""
|
107
|
+
].join("\x0D\x0A")
|
108
|
+
|
109
|
+
headers = @headers.merge(
|
110
|
+
"Content-Type" => "multipart/form-data; boundary=#{BOUNDARY}"
|
111
|
+
)
|
112
|
+
|
113
|
+
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
|
114
|
+
http.post(target + query, data, headers)
|
115
|
+
end
|
116
|
+
|
117
|
+
if first_file then
|
118
|
+
release_id = release_response.body[/release_id=(\d+)/, 1]
|
119
|
+
raise("Couldn't get release id") unless release_id
|
120
|
+
end
|
121
|
+
end
|
122
|
+
puts "Done!"
|
123
|
+
end
|
124
|
+
|
125
|
+
def publish_news(subject, details)
|
126
|
+
puts "About to publish news"
|
127
|
+
puts "Subject: '#{subject}'"
|
128
|
+
puts "Details:"
|
129
|
+
puts details
|
130
|
+
puts ""
|
131
|
+
|
132
|
+
release_response = Net::HTTP.start(@host, 80) do |http|
|
133
|
+
query_hash = {
|
134
|
+
"group_id" => @project.group_id,
|
135
|
+
"package_id" => package_id,
|
136
|
+
"post_changes" => "y",
|
137
|
+
"summary" => subject,
|
138
|
+
"details" => details
|
139
|
+
}
|
140
|
+
|
141
|
+
target = "/news/submit.php"
|
142
|
+
headers = @headers.merge(
|
143
|
+
"Content-Type" => "multipart/form-data"
|
144
|
+
)
|
145
|
+
http.post(target + query(query_hash), "", headers)
|
146
|
+
|
147
|
+
end
|
148
|
+
puts "Done!"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def query(query_hash)
|
154
|
+
"?" + query_hash.map do |(name, value)|
|
155
|
+
[name, URI.encode(value.to_s)].join("=")
|
156
|
+
end.join("&")
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'meta_project/tracker/xforge'
|
2
|
+
|
3
|
+
module MetaProject
|
4
|
+
module Project
|
5
|
+
module XForge
|
6
|
+
class SourceForge < XForgeBase
|
7
|
+
|
8
|
+
def initialize(unix_name, cvs_mod=nil)
|
9
|
+
super("sourceforge.net", unix_name, cvs_mod)
|
10
|
+
end
|
11
|
+
|
12
|
+
def tracker_class
|
13
|
+
::MetaProject::Tracker::XForge::SourceForgeTracker
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def create_cvs(unix_name, mod)
|
19
|
+
RSCM::Cvs.new(":pserver:anonymous@cvs.sourceforge.net:/cvsroot/#{unix_name}", mod)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_view_cvs(unix_name, mod)
|
23
|
+
view_cvs = "http://cvs.sourceforge.net/viewcvs.py/"
|
24
|
+
unix_name_mod = "#{unix_name}/#{mod}"
|
25
|
+
project_path = "#{unix_name_mod}/\#{path}"
|
26
|
+
rev = "rev=\#{revision}"
|
27
|
+
|
28
|
+
overview = "#{view_cvs}#{unix_name_mod}/"
|
29
|
+
history = "#{view_cvs}#{project_path}"
|
30
|
+
raw = "#{view_cvs}*checkout*/#{project_path}?#{rev}"
|
31
|
+
html = "#{history}?#{rev}&view=markup"
|
32
|
+
diff = "#{history}?r1=\#{previous_revision}&r2=\#{revision}"
|
33
|
+
|
34
|
+
ScmWeb.new(overview, history, raw, html, diff)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Regexp used to find projects' home page
|
38
|
+
def home_page_regexp
|
39
|
+
# This seems a little volatile
|
40
|
+
/<A href=\"(\w*:\/\/[^\"]*)\"> Project Home Page<\/A>/
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Project
|
3
|
+
module XForge
|
4
|
+
|
5
|
+
class XFile # :nodoc:
|
6
|
+
|
7
|
+
# extension => [mime_type, rubyforge_bin_type_id, rubyforge_src_type_id]
|
8
|
+
FILE_TYPES = {
|
9
|
+
".deb" => ["application/octet-stream", 1000],
|
10
|
+
|
11
|
+
# all of these can be source or binary
|
12
|
+
".rpm" => ["application/octet-stream", 2000, 5100],
|
13
|
+
".zip" => ["application/octet-stream", 3000, 5000],
|
14
|
+
".bz2" => ["application/octet-stream", 3100, 5010],
|
15
|
+
".gz" => ["application/octet-stream", 3110, 5020],
|
16
|
+
".jpg" => ["application/octet-stream", 8000],
|
17
|
+
".jpeg" => ["application/octet-stream", 8000],
|
18
|
+
".txt" => ["text/plain", 8100, 8100],
|
19
|
+
".html" => ["text/html", 8200, 8200],
|
20
|
+
".pdf" => ["application/octet-stream", 8300],
|
21
|
+
".ebuild" => ["application/octet-stream", 1300],
|
22
|
+
".exe" => ["application/octet-stream", 1100],
|
23
|
+
".dmg" => ["application/octet-stream", 1200],
|
24
|
+
".gem" => ["application/octet-stream", 1400],
|
25
|
+
".sig" => ["application/octet-stream", 8150]
|
26
|
+
}
|
27
|
+
FILE_TYPES.default = ["application/octet-stream", 9999, 5900] # default to "other", "other source"
|
28
|
+
|
29
|
+
attr_reader :basename, :ext, :content_type, :bin_type_id, :src_type_id
|
30
|
+
|
31
|
+
def initialize(filename)
|
32
|
+
@filename = filename
|
33
|
+
@basename = File.basename(filename)
|
34
|
+
@ext = File.extname(filename)
|
35
|
+
@content_type = FILE_TYPES[@ext][0]
|
36
|
+
@bin_type_id = FILE_TYPES[@ext][1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def data
|
40
|
+
File.open(@filename, "rb") { |file| file.read }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
module MetaProject
|
6
|
+
module Project
|
7
|
+
module XForge
|
8
|
+
class XForgeBase < Base
|
9
|
+
|
10
|
+
def initialize(host, unix_name, cvs_mod)
|
11
|
+
@host = host
|
12
|
+
@unix_name = unix_name
|
13
|
+
|
14
|
+
@tracker = tracker_class.new(group_id_uri("tracker"), self)
|
15
|
+
|
16
|
+
unless(cvs_mod.nil?)
|
17
|
+
@scm = create_cvs(unix_name, cvs_mod)
|
18
|
+
@scm_web = create_view_cvs(unix_name, cvs_mod)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_yaml_properties
|
23
|
+
["@host", "@unix_name"]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Logs in and returns a Session
|
27
|
+
def login(user_name, password)
|
28
|
+
http = Net::HTTP.new(@host, 80)
|
29
|
+
|
30
|
+
login_response = http.start do |http|
|
31
|
+
data = [
|
32
|
+
"login=1",
|
33
|
+
"form_loginname=#{user_name}",
|
34
|
+
"form_pw=#{password}"
|
35
|
+
].join("&")
|
36
|
+
http.post("/account/login.php", data)
|
37
|
+
end
|
38
|
+
|
39
|
+
cookie = login_response["set-cookie"]
|
40
|
+
raise "Login failed" unless cookie
|
41
|
+
Session.new(@host, self, cookie)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The group_id of this project
|
45
|
+
def group_id
|
46
|
+
unless(@group_id)
|
47
|
+
regexp = /stats\/[?&]group_id=(\d+)/
|
48
|
+
html = open(project_uri) { |data| data.read }
|
49
|
+
@group_id = html[regexp, 1]
|
50
|
+
raise "Couldn't get group_id" unless @group_id
|
51
|
+
end
|
52
|
+
@group_id
|
53
|
+
end
|
54
|
+
|
55
|
+
def project_uri
|
56
|
+
"http://#{@host}/projects/#{@unix_name}/"
|
57
|
+
end
|
58
|
+
|
59
|
+
def group_id_uri(path, postfix="")
|
60
|
+
"http://#{@host}/#{path}/?group_id=#{group_id}#{postfix}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# The home page of this project
|
64
|
+
def home_page
|
65
|
+
unless(@home_page)
|
66
|
+
html = open(project_uri) { |data| data.read }
|
67
|
+
@home_page = html[home_page_regexp, 1]
|
68
|
+
raise "Couldn't get home_page" unless @home_page
|
69
|
+
end
|
70
|
+
@home_page
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module ProjectAnalyzer
|
3
|
+
# Creates a project from an scm web url. The project has a +tracker+, +scm+ and +scm_web+.
|
4
|
+
def project_from_scm_web(url, options=nil)
|
5
|
+
# RubyForge
|
6
|
+
if(url =~ /http:\/\/rubyforge.org\/cgi-bin\/viewcvs.cgi\/(.*)[\/]?\?cvsroot=(.*)/)
|
7
|
+
unix_name = $2
|
8
|
+
mod = $1[-1..-1] == "/" ? $1[0..-2] : $1
|
9
|
+
return Project::XForge::RubyForge.new(unix_name, mod)
|
10
|
+
end
|
11
|
+
|
12
|
+
# SourceForge
|
13
|
+
if(url =~ /http:\/\/cvs.sourceforge.net\/viewcvs.py\/([^\/]*)\/(.*)/)
|
14
|
+
unix_name = $1
|
15
|
+
mod = $2[-1..-1] == "/" ? $2[0..-2] : $2
|
16
|
+
return Project::XForge::SourceForge.new(unix_name, mod)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Trac
|
20
|
+
if(url =~ /(http:\/\/.*)\/browser\/(.*)/)
|
21
|
+
trac_base_url = $1
|
22
|
+
svn_path = $2[-1..-1] == "/" ? $2[0..-2] : $2
|
23
|
+
return Project::Trac::TracProject.new(trac_base_url, options[:svn_root_url], svn_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Codehaus SVN
|
27
|
+
if(url =~ /http:\/\/svn.(.*).codehaus.org\/(.*)/)
|
28
|
+
svn_id = $1
|
29
|
+
svn_path = $2[-1..-1] == "/" ? $2[0..-2] : $2
|
30
|
+
return Project::Codehaus::CodehausProjectSvn.new(svn_id, svn_path, options[:jira_id])
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module MetaProject
|
2
|
+
|
3
|
+
# An ScmWeb instance is capable of generating URLs to various files and diffs
|
4
|
+
# in an online scm web interface.
|
5
|
+
class ScmWeb
|
6
|
+
|
7
|
+
# The variables to use in +uri_specs+ are:
|
8
|
+
#
|
9
|
+
# * path
|
10
|
+
# * revision
|
11
|
+
# * previous_revision
|
12
|
+
#
|
13
|
+
def initialize(overview_spec, history_spec, raw_spec, html_spec, diff_spec)
|
14
|
+
@overview_spec, @history_spec, @raw_spec, @html_spec, @diff_spec = overview_spec, history_spec, raw_spec, html_spec, diff_spec
|
15
|
+
end
|
16
|
+
|
17
|
+
def overview
|
18
|
+
file_uri(nil, nil, @overview_spec)
|
19
|
+
end
|
20
|
+
|
21
|
+
def history(path)
|
22
|
+
file_uri(path, nil, @history_spec)
|
23
|
+
end
|
24
|
+
|
25
|
+
def raw(path, revision)
|
26
|
+
file_uri(path, revision, @raw_spec)
|
27
|
+
end
|
28
|
+
|
29
|
+
def html(path, revision)
|
30
|
+
file_uri(path, revision, @html_spec)
|
31
|
+
end
|
32
|
+
|
33
|
+
def diff(path, revision, previous_revision)
|
34
|
+
file_uri(path, revision, @diff_spec, previous_revision)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Returns the file URI for +path+. Valid options are:
|
40
|
+
#
|
41
|
+
# * :type => ["overview"|"html"|"diff"|"raw"]
|
42
|
+
# * :revision
|
43
|
+
# * :previous_revision
|
44
|
+
def file_uri(path="", revision=nil, spec=@overview_spec, previous_revision=nil)
|
45
|
+
begin
|
46
|
+
eval("\"#{spec}\"", binding)
|
47
|
+
rescue NameError
|
48
|
+
raise "Couldn't evaluate '#{spec}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
|
4
|
+
# Tracker objects are responsible for interacting with issue trackers (bug trackers).
|
5
|
+
# They know how to recognise issue identifiers in strings (typically from SCM commit
|
6
|
+
# messages) and turn these into HTML links that point to the associated issue on an
|
7
|
+
# issue tracker installation running somewhere else.
|
8
|
+
class Base
|
9
|
+
def self.classes
|
10
|
+
[
|
11
|
+
::Tracker::Jira::JiraProject,
|
12
|
+
::Tracker::XForge::RubyForgeProject,
|
13
|
+
::Tracker::Trac::TracProject
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
# This module should be included by trackers that follow a digit-based issue scheme
|
4
|
+
module DigitIssues
|
5
|
+
def identifier_regexp
|
6
|
+
/#(\d+)/
|
7
|
+
end
|
8
|
+
|
9
|
+
def identifier_examples
|
10
|
+
["#1926", "#1446"]
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: find a way to extract just the issue summaries so they can be stored in dc as an array
|
14
|
+
# embedded in the revision object. that way we don't alter the original commit message
|
15
|
+
def markup(text)
|
16
|
+
text.gsub(identifier_regexp) do |match|
|
17
|
+
issue_identifier = $1
|
18
|
+
issue = issue(issue_identifier)
|
19
|
+
issue ? "<a href=\"#{issue.uri}\">#{issue.summary}</a>" : "\##{issue_identifier}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'xmlrpc/client'
|
2
|
+
|
3
|
+
module MetaProject
|
4
|
+
module Tracker
|
5
|
+
module Jira
|
6
|
+
class JiraTracker
|
7
|
+
JIRA_API = "jira1"
|
8
|
+
|
9
|
+
def initialize(rooturl=nil, identifier=nil, username=nil, password=nil)
|
10
|
+
@rooturl, @identifier, @username, @password = rooturl, identifier, username, password
|
11
|
+
end
|
12
|
+
|
13
|
+
def identifier_regexp
|
14
|
+
/([A-Z]+-[\d]+)/
|
15
|
+
end
|
16
|
+
|
17
|
+
def identifier_examples
|
18
|
+
["DC-420", "PICO-12"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def overview
|
22
|
+
"#{@rooturl}/browse/#{@identifier}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def issue(issue_identifier)
|
26
|
+
session = login
|
27
|
+
begin
|
28
|
+
issue = session.getIssue(issue_identifier)
|
29
|
+
Issue.new("#{@rooturl}/browse/#{issue_identifier}", issue["summary"])
|
30
|
+
rescue XMLRPC::FaultException
|
31
|
+
# Probably bad issue number
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def markup(text)
|
37
|
+
text.gsub(identifier_regexp) do |match|
|
38
|
+
issue_identifier = $1
|
39
|
+
issue = issue(issue_identifier)
|
40
|
+
issue ? "<a href=\"#{issue.uri}\">#{issue_identifier}: #{issue.summary}</a>" : issue_identifier
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def login
|
47
|
+
client = XMLRPC::Client.new2("#{@rooturl}/rpc/xmlrpc")
|
48
|
+
token = client.call("#{JIRA_API}.login", @username, @password)
|
49
|
+
Session.new(client, token)
|
50
|
+
end
|
51
|
+
|
52
|
+
# This wrapper around XMLRPC::Client that allows simpler method calls
|
53
|
+
# via method_missing and doesn't require to manage the token
|
54
|
+
class Session
|
55
|
+
def initialize(client, token)
|
56
|
+
@client, @token = client, token
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(sym, args, &block)
|
60
|
+
token_args = [@token] << args
|
61
|
+
xmlrpc_method = "#{JIRA_API}.#{sym.to_s}"
|
62
|
+
@client.call(xmlrpc_method, *token_args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'meta_project/tracker/jira/jira_tracker'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module Trac
|
4
|
+
class TracTracker < ::MetaProject::Tracker::Base
|
5
|
+
include ::MetaProject::Tracker::DigitIssues
|
6
|
+
|
7
|
+
def initialize(trac_base_url)
|
8
|
+
@trac_base_url = trac_base_url
|
9
|
+
end
|
10
|
+
|
11
|
+
def overview
|
12
|
+
"#{@trac_base_url}/report"
|
13
|
+
end
|
14
|
+
|
15
|
+
def issue(issue_identifier)
|
16
|
+
issue_uri = "#{@trac_base_url}/ticket/#{issue_identifier}"
|
17
|
+
begin
|
18
|
+
html = open(issue_uri) { |data| data.read }
|
19
|
+
summary = html[/Ticket ##{issue_identifier}\s*<\/h1>\s*<h2>([^<]*)<\/h2>/n, 1]
|
20
|
+
::MetaProject::Tracker::Issue.new(issue_uri, summary)
|
21
|
+
rescue OpenURI::HTTPError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'meta_project/tracker/trac/trac_tracker'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module XForge
|
4
|
+
class RubyForgeTracker < XForgeTracker
|
5
|
+
|
6
|
+
def subtracker_regexp
|
7
|
+
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
+
end
|
9
|
+
|
10
|
+
def issue_regexp(identifier)
|
11
|
+
/<a href=\"\/tracker\/index.php\?func=detail&aid=#{identifier}&group_id=\d+&atid=\d+\">([^<]*)<\/a>/
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module XForge
|
4
|
+
class SourceForgeTracker < XForgeTracker
|
5
|
+
|
6
|
+
def subtracker_regexp
|
7
|
+
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
+
end
|
9
|
+
|
10
|
+
def issue_regexp(identifier)
|
11
|
+
/<a href=\"\/tracker\/index.php\?func=detail&aid=#{identifier}&group_id=\d+&atid=\d+\">([^<]*)<\/a>/
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|