xforge 0.3.5 → 0.4.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/CHANGES +34 -0
- data/Rakefile +5 -5
- data/lib/meta_project.rb +6 -0
- data/lib/meta_project/project.rb +4 -0
- data/lib/meta_project/project/base.rb +7 -0
- data/lib/meta_project/project/codehaus.rb +1 -0
- data/lib/meta_project/project/codehaus/codehaus_project_svn.rb +26 -0
- data/lib/meta_project/project/trac.rb +1 -0
- data/lib/meta_project/project/trac/trac_project.rb +26 -0
- data/lib/meta_project/project/xforge.rb +5 -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_analyzer.rb +36 -0
- data/lib/meta_project/scm_web.rb +53 -0
- data/lib/meta_project/tracker.rb +6 -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.rb +1 -0
- data/lib/meta_project/tracker/jira/jira_tracker.rb +68 -0
- data/lib/meta_project/tracker/trac.rb +1 -0
- data/lib/meta_project/tracker/trac/trac_tracker.rb +29 -0
- data/lib/meta_project/tracker/xforge.rb +3 -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/{xforge → meta_project}/version_parser.rb +6 -6
- data/lib/rake/contrib/xforge/base.rb +2 -3
- data/lib/rake/contrib/xforge/news_publisher.rb +1 -5
- data/lib/rake/contrib/xforge/release.rb +3 -7
- data/lib/xforge.rb +0 -1
- metadata +30 -27
- data/lib/scm_web/base.rb +0 -12
- data/lib/scm_web/file_uri.rb +0 -21
- data/lib/scm_web/view_cvs.rb +0 -44
- data/lib/tracker/base.rb +0 -16
- data/lib/tracker/bugzilla/project.rb +0 -8
- data/lib/tracker/digit_issues.rb +0 -22
- data/lib/tracker/fog_bugz/project.rb +0 -8
- data/lib/tracker/issue.rb +0 -9
- data/lib/tracker/jira.rb +0 -2
- data/lib/tracker/jira/host.rb +0 -40
- data/lib/tracker/jira/project.rb +0 -43
- data/lib/tracker/mantis/project.rb +0 -8
- data/lib/tracker/scarab/project.rb +0 -8
- data/lib/tracker/trac.rb +0 -1
- data/lib/tracker/trac/project.rb +0 -35
- data/lib/tracker/xforge.rb +0 -2
- data/lib/tracker/xforge/base.rb +0 -77
- data/lib/tracker/xforge/rubyforge.rb +0 -7
- data/lib/xforge/host.rb +0 -26
- data/lib/xforge/project.rb +0 -87
- data/lib/xforge/rubyforge.rb +0 -50
- data/lib/xforge/session.rb +0 -158
- data/lib/xforge/sourceforge.rb +0 -46
- data/lib/xforge/xfile.rb +0 -41
@@ -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 @@
|
|
1
|
+
require 'meta_project/tracker/jira/jira_tracker'
|
@@ -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/trac/trac_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,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
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'meta_project/tracker/base'
|
2
|
+
require 'meta_project/tracker/digit_issues'
|
3
|
+
require 'meta_project/tracker/issue'
|
4
|
+
|
5
|
+
module MetaProject
|
6
|
+
module Tracker
|
7
|
+
module XForge
|
8
|
+
class XForgeTracker < Base
|
9
|
+
include ::MetaProject::Tracker::DigitIssues
|
10
|
+
|
11
|
+
attr_accessor :overview, :project
|
12
|
+
|
13
|
+
# TODO: don't pass in project!! pass in hostname and id!
|
14
|
+
def initialize(overview, project)
|
15
|
+
@overview, @project = overview, project
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finds an Issue by +identifier+
|
19
|
+
def issue(identifier)
|
20
|
+
sub_trackers = atids.collect {|atid| SubTracker.new(self, atid)}
|
21
|
+
sub_trackers.each do |sub_tracker|
|
22
|
+
issue = sub_tracker.issue(identifier)
|
23
|
+
return issue unless issue.nil?
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def markup(text)
|
29
|
+
text.gsub(identifier_regexp) do |match|
|
30
|
+
issue_identifier = $1
|
31
|
+
issue = issue(issue_identifier)
|
32
|
+
issue ? "<a href=\"#{issue.uri}\">\##{issue_identifier}: #{issue.summary}</a>" : "\##{issue_identifier}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class SubTracker
|
37
|
+
attr_reader :uri
|
38
|
+
|
39
|
+
def initialize(tracker, atid)
|
40
|
+
@tracker = tracker
|
41
|
+
@atid = atid
|
42
|
+
# FIXME: This will only show open items.
|
43
|
+
@uri = "#{tracker.overview}&atid=#{atid}&func=browse"
|
44
|
+
end
|
45
|
+
|
46
|
+
def issue(identifier)
|
47
|
+
html = open(uri) { |data| data.read }
|
48
|
+
|
49
|
+
regexp = @tracker.issue_regexp(identifier)
|
50
|
+
if(html =~ regexp)
|
51
|
+
issue_uri = @tracker.project.group_id_uri("tracker/index.php", "&atid=#{@atid}&func=detail&aid=#{identifier}")
|
52
|
+
return Issue.new(issue_uri, $1)
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# The ids of the subtrackers
|
61
|
+
def atids
|
62
|
+
html = open(overview) { |data| data.read }
|
63
|
+
|
64
|
+
# TODO: there has to be a better way to extract the atids from the HTML!
|
65
|
+
atids = []
|
66
|
+
offset = 0
|
67
|
+
look_for_atid = true
|
68
|
+
while(look_for_atid)
|
69
|
+
match_data = subtracker_regexp.match(html[offset..-1])
|
70
|
+
if(match_data)
|
71
|
+
offset += match_data.begin(1)
|
72
|
+
atids << match_data[1]
|
73
|
+
else
|
74
|
+
look_for_atid = false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
atids
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|