meta_project 0.4.11 → 0.4.12
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +277 -269
- data/MIT-LICENSE +21 -21
- data/README +126 -126
- data/Rakefile +152 -152
- data/doc/base_attrs.rdoc +2 -2
- data/lib/meta_project.rb +11 -10
- data/lib/meta_project/core_ext/open_uri.rb +22 -22
- data/lib/meta_project/core_ext/pathname.rb +36 -36
- data/lib/meta_project/core_ext/string.rb +4 -4
- data/lib/meta_project/http/multipart.rb +32 -0
- data/lib/meta_project/patois/parser.rb +98 -98
- data/lib/meta_project/project.rb +4 -4
- data/lib/meta_project/project/base.rb +8 -8
- data/lib/meta_project/project/codehaus.rb +1 -1
- data/lib/meta_project/project/codehaus/codehaus_project_svn.rb +30 -30
- data/lib/meta_project/project/trac.rb +1 -1
- data/lib/meta_project/project/trac/trac_project.rb +53 -53
- data/lib/meta_project/project/xforge.rb +5 -5
- data/lib/meta_project/project/xforge/ruby_forge.rb +46 -48
- data/lib/meta_project/project/xforge/session.rb +177 -191
- data/lib/meta_project/project/xforge/source_forge.rb +49 -49
- data/lib/meta_project/project/xforge/xfile.rb +44 -44
- data/lib/meta_project/project/xforge/xforge_base.rb +81 -79
- data/lib/meta_project/project_analyzer.rb +35 -35
- data/lib/meta_project/release/freshmeat.rb +267 -267
- data/lib/meta_project/release/raa.rb +572 -572
- data/lib/meta_project/scm_web.rb +1 -1
- data/lib/meta_project/scm_web/browser.rb +111 -111
- data/lib/meta_project/scm_web/pathname.rb +88 -88
- data/lib/meta_project/tracker.rb +6 -6
- data/lib/meta_project/tracker/base.rb +23 -23
- data/lib/meta_project/tracker/digit_issues.rb +33 -32
- data/lib/meta_project/tracker/issue.rb +56 -52
- data/lib/meta_project/tracker/jira.rb +2 -2
- data/lib/meta_project/tracker/jira/jira_issues.rb +34 -33
- data/lib/meta_project/tracker/jira/jira_tracker.rb +123 -123
- data/lib/meta_project/tracker/trac.rb +1 -1
- data/lib/meta_project/tracker/trac/trac_tracker.rb +32 -32
- data/lib/meta_project/tracker/xforge.rb +3 -3
- data/lib/meta_project/tracker/xforge/ruby_forge_tracker.rb +17 -17
- data/lib/meta_project/tracker/xforge/source_forge_tracker.rb +17 -17
- data/lib/meta_project/tracker/xforge/xforge_tracker.rb +190 -105
- data/lib/meta_project/version_parser.rb +52 -52
- data/lib/rake/contrib/xforge.rb +3 -3
- data/lib/rake/contrib/xforge/base.rb +64 -64
- data/lib/rake/contrib/xforge/news_publisher.rb +97 -97
- data/lib/rake/contrib/xforge/release.rb +134 -134
- metadata +3 -3
- data/TODO +0 -9
@@ -1 +1 @@
|
|
1
|
-
require 'meta_project/tracker/trac/trac_tracker'
|
1
|
+
require 'meta_project/tracker/trac/trac_tracker'
|
@@ -1,33 +1,33 @@
|
|
1
|
-
module MetaProject
|
2
|
-
module Tracker
|
3
|
-
module Trac
|
4
|
-
class TracTracker < Base
|
5
|
-
include DigitIssues
|
6
|
-
|
7
|
-
attr_accessor :trac_base_url
|
8
|
-
|
9
|
-
def initialize(trac_base_url=nil)
|
10
|
-
@trac_base_url = trac_base_url
|
11
|
-
end
|
12
|
-
|
13
|
-
def overview
|
14
|
-
"#{@trac_base_url}/report"
|
15
|
-
end
|
16
|
-
|
17
|
-
def materialize(issue)
|
18
|
-
begin
|
19
|
-
url = "#{@trac_base_url}/ticket/#{issue.identifier}"
|
20
|
-
html = better_open(url).read
|
21
|
-
summary = html[/Ticket ##{issue.identifier}\s*<\/h1>\s*<h2>([^<]*)<\/h2>/n, 1]
|
22
|
-
issue.attributes[:summary] = summary
|
23
|
-
issue.attributes[:url] = url
|
24
|
-
rescue OpenURI::HTTPError => e
|
25
|
-
STDERR.puts e.message
|
26
|
-
end
|
27
|
-
issue
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module Trac
|
4
|
+
class TracTracker < Base
|
5
|
+
include DigitIssues
|
6
|
+
|
7
|
+
attr_accessor :trac_base_url
|
8
|
+
|
9
|
+
def initialize(trac_base_url=nil)
|
10
|
+
@trac_base_url = trac_base_url
|
11
|
+
end
|
12
|
+
|
13
|
+
def overview
|
14
|
+
"#{@trac_base_url}/report"
|
15
|
+
end
|
16
|
+
|
17
|
+
def materialize(issue)
|
18
|
+
begin
|
19
|
+
url = "#{@trac_base_url}/ticket/#{issue.identifier}"
|
20
|
+
html = better_open(url).read
|
21
|
+
summary = html[/Ticket ##{issue.identifier}\s*<\/h1>\s*<h2>([^<]*)<\/h2>/n, 1]
|
22
|
+
issue.attributes[:summary] = summary
|
23
|
+
issue.attributes[:url] = url
|
24
|
+
rescue OpenURI::HTTPError => e
|
25
|
+
STDERR.puts e.message
|
26
|
+
end
|
27
|
+
issue
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
33
|
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
require 'meta_project/tracker/xforge/xforge_tracker'
|
2
|
-
require 'meta_project/tracker/xforge/ruby_forge_tracker'
|
3
|
-
require 'meta_project/tracker/xforge/source_forge_tracker'
|
1
|
+
require 'meta_project/tracker/xforge/xforge_tracker'
|
2
|
+
require 'meta_project/tracker/xforge/ruby_forge_tracker'
|
3
|
+
require 'meta_project/tracker/xforge/source_forge_tracker'
|
@@ -1,17 +1,17 @@
|
|
1
|
-
module MetaProject
|
2
|
-
module Tracker
|
3
|
-
module XForge
|
4
|
-
class RubyForgeTracker < XForgeTracker
|
5
|
-
|
6
|
-
def subtracker_pattern
|
7
|
-
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
-
end
|
9
|
-
|
10
|
-
def issue_summary_pattern(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
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module XForge
|
4
|
+
class RubyForgeTracker < XForgeTracker
|
5
|
+
|
6
|
+
def subtracker_pattern
|
7
|
+
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
+
end
|
9
|
+
|
10
|
+
def issue_summary_pattern(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
|
@@ -1,17 +1,17 @@
|
|
1
|
-
module MetaProject
|
2
|
-
module Tracker
|
3
|
-
module XForge
|
4
|
-
class SourceForgeTracker < XForgeTracker
|
5
|
-
|
6
|
-
def subtracker_pattern
|
7
|
-
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
-
end
|
9
|
-
|
10
|
-
def issue_summary_pattern(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
|
1
|
+
module MetaProject
|
2
|
+
module Tracker
|
3
|
+
module XForge
|
4
|
+
class SourceForgeTracker < XForgeTracker
|
5
|
+
|
6
|
+
def subtracker_pattern
|
7
|
+
/\/tracker\/\?atid=(\d+)&group_id=\d*&func=browse/
|
8
|
+
end
|
9
|
+
|
10
|
+
def issue_summary_pattern(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
|
@@ -1,106 +1,191 @@
|
|
1
|
-
|
2
|
-
require 'meta_project/tracker/base'
|
3
|
-
require 'meta_project/tracker/digit_issues'
|
4
|
-
require 'meta_project/tracker/issue'
|
5
|
-
|
6
|
-
module MetaProject
|
7
|
-
module Tracker
|
8
|
-
module XForge
|
9
|
-
class XForgeTracker < Base
|
10
|
-
include DigitIssues
|
11
|
-
|
12
|
-
attr_accessor :overview, :project
|
13
|
-
|
14
|
-
# TODO: don't pass in project!! pass in hostname and id! This won't work from DC!!
|
15
|
-
def initialize(overview=nil, project=nil)
|
16
|
-
@overview, @project = overview, project
|
17
|
-
end
|
18
|
-
|
19
|
-
# Finds an Issue by +identifier+
|
20
|
-
def issue(identifier)
|
21
|
-
sub_trackers
|
22
|
-
|
23
|
-
issue
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
1
|
+
|
2
|
+
require 'meta_project/tracker/base'
|
3
|
+
require 'meta_project/tracker/digit_issues'
|
4
|
+
require 'meta_project/tracker/issue'
|
5
|
+
|
6
|
+
module MetaProject
|
7
|
+
module Tracker
|
8
|
+
module XForge
|
9
|
+
class XForgeTracker < Base
|
10
|
+
include DigitIssues
|
11
|
+
|
12
|
+
attr_accessor :overview, :project
|
13
|
+
|
14
|
+
# TODO: don't pass in project!! pass in hostname and id! This won't work from DC!!
|
15
|
+
def initialize(overview=nil, project=nil)
|
16
|
+
@overview, @project = overview, project
|
17
|
+
end
|
18
|
+
|
19
|
+
# Finds an Issue by +identifier+
|
20
|
+
def issue(identifier)
|
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 materialize(issue)
|
29
|
+
issue
|
30
|
+
end
|
31
|
+
|
32
|
+
def create(issue, user_name, password)
|
33
|
+
# TODO: get the subtracker atid or name from the issue's options
|
34
|
+
subtracker = sub_trackers[0]
|
35
|
+
subtracker.create(issue, user_name, password)
|
36
|
+
end
|
37
|
+
|
38
|
+
class SubTracker
|
39
|
+
include HTTP::Multipart
|
40
|
+
|
41
|
+
# Issue modification constants
|
42
|
+
OPEN = 1
|
43
|
+
CLOSED = 2
|
44
|
+
DELETED = 3
|
45
|
+
|
46
|
+
def initialize(tracker, atid)
|
47
|
+
@tracker = tracker
|
48
|
+
@atid = atid
|
49
|
+
# FIXME: This will only show open items.
|
50
|
+
@baseurl = "#{tracker.overview}&atid=#{atid}"
|
51
|
+
@overview = "#{@baseurl}&func=browse"
|
52
|
+
end
|
53
|
+
|
54
|
+
def issue(identifier)
|
55
|
+
html = better_open(@overview).read
|
56
|
+
|
57
|
+
issue_summary_pattern = @tracker.issue_summary_pattern(identifier)
|
58
|
+
if(html =~ issue_summary_pattern)
|
59
|
+
issue_url = @tracker.project.group_id_uri("tracker/index.php", "&atid=#{@atid}&func=detail&aid=#{identifier}")
|
60
|
+
issue_summary = $1.strip
|
61
|
+
return Issue.new(self, :identifier => identifier,:summary => issue_summary, :url => issue_url)
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def create(issue, user_name, password)
|
68
|
+
session = @tracker.project.login(user_name, password)
|
69
|
+
|
70
|
+
Net::HTTP.start(@tracker.project.host, 80) do |http|
|
71
|
+
query_hash = {
|
72
|
+
"func" => "postadd",
|
73
|
+
"category_id" => "100", # seems to be standard for all trackers
|
74
|
+
"artifact_group_id" => "100", # seems to be standard for all trackers
|
75
|
+
"summary" => issue.summary,
|
76
|
+
"details" => issue.detail,
|
77
|
+
"user_email" => "",
|
78
|
+
}
|
79
|
+
|
80
|
+
target = "/news/submit.php" #TODO: use this?
|
81
|
+
response = post_multipart(http, @baseurl, query_hash, session.request_headers)
|
82
|
+
|
83
|
+
# the post brings us back to the overview page, where the new issue is the last one
|
84
|
+
response.body.scan(/aid=([\d]+)/) do |a|
|
85
|
+
issue.attributes[:identifier] = a[0]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
issue
|
89
|
+
end
|
90
|
+
|
91
|
+
def close(issue, user_name, password)
|
92
|
+
modify(issue, user_name, password, CLOSED)
|
93
|
+
end
|
94
|
+
|
95
|
+
def delete(issue, user_name, password)
|
96
|
+
modify(issue, user_name, password, DELETE)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def modify(issue, user_name, password, status)
|
102
|
+
session = @tracker.project.login(user_name, password)
|
103
|
+
|
104
|
+
Net::HTTP.start(@tracker.project.host, 80) do |http|
|
105
|
+
query_hash = {
|
106
|
+
"group_id" => @tracker.project.group_id,
|
107
|
+
"atid" => @atid,
|
108
|
+
"func" => "postmod",
|
109
|
+
"$result[]" => "",
|
110
|
+
"artifact_id" => issue.identifier,
|
111
|
+
"new_artifact_type_id" => @atid,
|
112
|
+
"category_id" => "100", # None
|
113
|
+
"artifact_group_id" => "100", # None
|
114
|
+
"assigned_to" => "100", # None
|
115
|
+
"priority" => "3", # None
|
116
|
+
"status_id" => status,
|
117
|
+
"resolution_id" => "100",
|
118
|
+
"canned_respnse" => "100",
|
119
|
+
"summary" => issue.summary,
|
120
|
+
"details" => issue.detail
|
121
|
+
}
|
122
|
+
|
123
|
+
target = "/tracker/index.php"
|
124
|
+
response = post_multipart(http, target, query_hash, session.request_headers)
|
125
|
+
|
126
|
+
close_pattern = /Updated successfully/
|
127
|
+
unless(response.body =~ close_pattern)
|
128
|
+
File.open("xforge_close.html", "w") {|io| io.write response.body}
|
129
|
+
STDERR.puts "WARNING: Failed to close issue \##{issue.identifier}. I was looking for /#{close_pattern.source}/. Response written to xforge_close.html"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
issue
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def sub_trackers
|
139
|
+
@sub_trackers ||= atids.collect {|atid| SubTracker.new(self, atid)}
|
140
|
+
end
|
141
|
+
|
142
|
+
# The ids of the subtrackers
|
143
|
+
def atids
|
144
|
+
# headers = {
|
145
|
+
# "User-Agent" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4",
|
146
|
+
# "Accept" => "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
|
147
|
+
# "Accept-Language" => "en-us,en;q=0.5",
|
148
|
+
# "Accept-Encoding" => "gzip,deflate",
|
149
|
+
# "Accept-Charset" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
|
150
|
+
# "Keep-Alive" => "300",
|
151
|
+
# "Connection" => "close",
|
152
|
+
# "X-Requested-With" => "XMLHttpRequest",
|
153
|
+
# "X-Prototype-Version" => "1.3.1",
|
154
|
+
# "Content-Type" => "application/x-www-form-urlencoded",
|
155
|
+
# "Content-Length" => "0",
|
156
|
+
# "Cookie" => "author=AnonymousCoward; _session_id=975a583d513190522ce3aeba315552d0",
|
157
|
+
# "Pragma" => "no-cache",
|
158
|
+
# "Cache-Control" => "no-cache"
|
159
|
+
# }
|
160
|
+
|
161
|
+
html = better_open(overview).read
|
162
|
+
STDERR.puts "The HTML returned from #{overview} was empty! This might be because the server is trying to fool us" if html == ""
|
163
|
+
|
164
|
+
# TODO: there has to be a better way to extract the atids from the HTML!
|
165
|
+
atids = []
|
166
|
+
offset = 0
|
167
|
+
look_for_atid = true
|
168
|
+
while(look_for_atid)
|
169
|
+
match_data = subtracker_pattern.match(html[offset..-1])
|
170
|
+
if(match_data)
|
171
|
+
offset += match_data.begin(1)
|
172
|
+
atids << match_data[1]
|
173
|
+
else
|
174
|
+
look_for_atid = false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
if atids.empty?
|
178
|
+
debug_file = "xforge_tracker_debug.html"
|
179
|
+
File.open(debug_file, "w"){|io| io.write html}
|
180
|
+
STDERR.puts "WARNING: No subtrackers found at #{overview}."
|
181
|
+
STDERR.puts "I was looking for /#{subtracker_pattern.source}/"
|
182
|
+
STDERR.puts "Please consider filing a bug report at http://rubyforge.org/tracker/?atid=3161&group_id=801&func=browse"
|
183
|
+
STDERR.puts "The HTML has been saved to #{debug_file}"
|
184
|
+
end
|
185
|
+
atids
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
106
191
|
end
|
@@ -1,52 +1,52 @@
|
|
1
|
-
module MetaProject
|
2
|
-
class VersionParser
|
3
|
-
def parse(changes_file, version)
|
4
|
-
release_notes_first = nil
|
5
|
-
changes_first = nil
|
6
|
-
changes_last = nil
|
7
|
-
|
8
|
-
lines = File.open(changes_file).readlines
|
9
|
-
state = nil
|
10
|
-
lines.each_with_index do |line, n|
|
11
|
-
# parse state
|
12
|
-
if (line =~ /^==/ && state == :in_release_notes && state != :done)
|
13
|
-
changes_first = changes_last = n
|
14
|
-
state = :done
|
15
|
-
end
|
16
|
-
if line =~ /#{version}/ && state.nil?
|
17
|
-
state = :in_release_notes
|
18
|
-
release_notes_first = n+1
|
19
|
-
end
|
20
|
-
if (line =~ /^\*/ && state == :in_release_notes)
|
21
|
-
state = :in_changes
|
22
|
-
changes_first = changes_last = n
|
23
|
-
end
|
24
|
-
if (line =~ /^\*/ && state == :in_changes)
|
25
|
-
changes_last = n
|
26
|
-
end
|
27
|
-
if (line =~ /^==/ && state == :in_changes)
|
28
|
-
state = :done
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
release_notes = lines[release_notes_first..changes_first-1].join("")
|
33
|
-
raise "Release notes for #{version} couldn't be parsed from #{changes_file}" if release_notes.strip == ""
|
34
|
-
|
35
|
-
release_changes = lines[changes_first..changes_last].collect do |line|
|
36
|
-
line.length >= 2 ? line[2..-1].chomp : line
|
37
|
-
end
|
38
|
-
raise "Release changes for #{version} couldn't be parsed from #{changes_file}" if release_changes.length == 0
|
39
|
-
|
40
|
-
Version.new(release_notes, release_changes)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class Version
|
45
|
-
attr_reader :release_notes # String
|
46
|
-
attr_reader :release_changes # Array of String
|
47
|
-
|
48
|
-
def initialize(release_notes, release_changes)
|
49
|
-
@release_notes, @release_changes = release_notes, release_changes
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
1
|
+
module MetaProject
|
2
|
+
class VersionParser
|
3
|
+
def parse(changes_file, version)
|
4
|
+
release_notes_first = nil
|
5
|
+
changes_first = nil
|
6
|
+
changes_last = nil
|
7
|
+
|
8
|
+
lines = File.open(changes_file).readlines
|
9
|
+
state = nil
|
10
|
+
lines.each_with_index do |line, n|
|
11
|
+
# parse state
|
12
|
+
if (line =~ /^==/ && state == :in_release_notes && state != :done)
|
13
|
+
changes_first = changes_last = n
|
14
|
+
state = :done
|
15
|
+
end
|
16
|
+
if line =~ /#{version}/ && state.nil?
|
17
|
+
state = :in_release_notes
|
18
|
+
release_notes_first = n+1
|
19
|
+
end
|
20
|
+
if (line =~ /^\*/ && state == :in_release_notes)
|
21
|
+
state = :in_changes
|
22
|
+
changes_first = changes_last = n
|
23
|
+
end
|
24
|
+
if (line =~ /^\*/ && state == :in_changes)
|
25
|
+
changes_last = n
|
26
|
+
end
|
27
|
+
if (line =~ /^==/ && state == :in_changes)
|
28
|
+
state = :done
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
release_notes = lines[release_notes_first..changes_first-1].join("")
|
33
|
+
raise "Release notes for #{version} couldn't be parsed from #{changes_file}" if release_notes.strip == ""
|
34
|
+
|
35
|
+
release_changes = lines[changes_first..changes_last].collect do |line|
|
36
|
+
line.length >= 2 ? line[2..-1].chomp : line
|
37
|
+
end
|
38
|
+
raise "Release changes for #{version} couldn't be parsed from #{changes_file}" if release_changes.length == 0
|
39
|
+
|
40
|
+
Version.new(release_notes, release_changes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Version
|
45
|
+
attr_reader :release_notes # String
|
46
|
+
attr_reader :release_changes # Array of String
|
47
|
+
|
48
|
+
def initialize(release_notes, release_changes)
|
49
|
+
@release_notes, @release_changes = release_notes, release_changes
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|