meta_project 0.4.3 → 0.4.4

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 CHANGED
@@ -1,12 +1,24 @@
1
- = XForge Changelog
1
+ = MetaProject Changelog
2
+
3
+ == Version 0.4.4
4
+
5
+ This release introduces a new Domain Specific Language (DSL) for SCM commit messages similar to Trac's post-commit hook for updating tickets.
6
+ Patois is supported in a tracker-agnostic way (although it's only half implemented for JIRA only ATM).
7
+ There is also initial support for FreshMeat and RAA thanks to Thomas Leitner.
8
+
9
+ * Added support for Patois parsing (See RubyDoc).
10
+ * Gave the Patois DSL its name.
11
+ * Improved the Tracker::Issue API to be more OO and uniform across trackers.
12
+ * Adds support for FreshMeat and RAA (Fixes #2323). This is not aligned with the rest of the MetaProject API yet.
2
13
 
3
14
  == Version 0.4.3
4
15
 
5
- This is a bugfix release.
16
+ This release fixes some bugs and implements some new issue tracker features.
6
17
 
7
18
  * Aligned properties between TracTracker and JiraTracker with ProjectAnalyzer.
8
19
  * Stripping leading and trailing whitespace from issue summaries (SF seems to have changed).
9
20
  * Added name to XForge projects.
21
+ * Added create_issue to JiraTracker.
10
22
 
11
23
  == Version 0.4.2
12
24
 
data/Rakefile CHANGED
@@ -24,7 +24,7 @@ require 'rake/rdoctask'
24
24
  #
25
25
  # REMEMBER TO KEEP PKG_VERSION IN SYNC WITH THE CHANGES FILE!
26
26
  PKG_NAME = "meta_project"
27
- PKG_VERSION = "0.4.3"
27
+ PKG_VERSION = "0.4.4"
28
28
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
29
29
  PKG_FILES = FileList[
30
30
  '[A-Z]*',
@@ -0,0 +1,99 @@
1
+ module MetaProject
2
+ # Patois is a domain specific language (DSL)
3
+ # for configuration and release management. It was first implemented by
4
+ # <a href="http://projects.edgewall.com/trac/browser/trunk/contrib/trac-post-commit-hook">Trac</a>
5
+ # and this class is a Ruby port of that Python code.
6
+ #
7
+ # A similar idea was created by the DamageControl team in <a href="http://jira.codehaus.org/browse/DC-159">DC-159</a>
8
+ # before it was implemented in Trac, but the Trac team beat the DamageControl to implement it.
9
+ #
10
+ # Anyhow, by giving this mini DSL a name and some better profiling we hope its adoption
11
+ # will increase. Patois means:
12
+ #
13
+ # * a dialect other than the standard or literary dialect
14
+ # * uneducated or provincial speech
15
+ # * the characteristic special language of an occupational or social group
16
+ #
17
+ # Patois' intended usage is in SCM commit messages. A patois-aware system can parse the commit
18
+ # messages and try to recognise patois expressions and take appropriate actions.
19
+ #
20
+ # Here is a little taste of what you can say and do with Patois.
21
+ #
22
+ # == General form
23
+ #
24
+ # command #1
25
+ # command #1, #2
26
+ # command #1 & #2
27
+ # command #1 and #2 # == Closing issues in an issue tracker
28
+ #
29
+ # You can have more than one command in a message. The following commands
30
+ # are supported. There is more than one spelling for each command, to make
31
+ # this as user-friendly as possible.
32
+ #
33
+ # close, closed, closes, fix, fixed, fixes
34
+ # The specified issues are closed with the contents of the
35
+ # commit message being added to it.
36
+ #
37
+ # addresses, re, ref, references, refs, see
38
+ # The specified issues are left in their current status, but
39
+ # the contents of the commit message are added to their notes.
40
+ #
41
+ # A fairly complicated example of what you can do is with a commit message
42
+ # of:
43
+ #
44
+ # Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
45
+ #
46
+ # This will close #10 and #12, and add a note to #12
47
+ #
48
+ module Patois
49
+
50
+ # Parses Patois
51
+ #
52
+ class Parser
53
+
54
+ SUPPORTED_COMMANDS = {
55
+ 'close' => ':close',
56
+ 'closed' => ':close',
57
+ 'closes' => ':close',
58
+ 'fix' => ':close',
59
+ 'fixed' => ':close',
60
+ 'fixes' => ':close',
61
+ 'addresses' => ':comment',
62
+ 're' => ':comment',
63
+ 'ref' => ':comment',
64
+ 'references' => ':comment',
65
+ 'refs' => ':comment',
66
+ 'see' => ':comment'
67
+ }
68
+
69
+ # Creates a new parser that will parse commands with +command_pattern+
70
+ # and emit individual commands with +issue_pattern+.
71
+ def initialize(command_pattern, issue_pattern)
72
+ @command_pattern = command_pattern
73
+ @issue_pattern = issue_pattern
74
+ end
75
+
76
+ # Parses a patois String and yields commands.
77
+ # Each operation can be executed with +execute+
78
+ def parse(msg)
79
+ msg.scan(@command_pattern) do |cmd_group|
80
+ cmd = SUPPORTED_COMMANDS[cmd_group[0].downcase]
81
+ if(cmd)
82
+ cmd_group[1].scan(@issue_pattern) do |issue_id_array|
83
+ yield Command.new(cmd, issue_id_array[0].upcase, msg)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ class Command
90
+
91
+ attr_reader :cmd, :issue_id, :msg
92
+
93
+ def initialize(cmd, issue_id, msg)
94
+ @cmd, @issue_id, @msg = cmd, issue_id, msg
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1 @@
1
+ require 'meta_project/patois/parser'
@@ -46,7 +46,7 @@ module MetaProject
46
46
  def group_id
47
47
  unless(@group_id)
48
48
  regexp = /stats\/[?&]group_id=(\d+)/
49
- html = open(xforge_project_url) { |data| data.read }
49
+ html = open(xforge_project_url).read
50
50
  @group_id = html[regexp, 1]
51
51
  raise "Couldn't get group_id" unless @group_id
52
52
  end
@@ -64,7 +64,7 @@ module MetaProject
64
64
  # The home page of this project
65
65
  def home_page
66
66
  unless(@home_page)
67
- html = open(xforge_project_url) { |data| data.read }
67
+ html = open(xforge_project_url).read
68
68
  @home_page = html[home_page_regexp, 1]
69
69
  raise "Couldn't get home_page" unless @home_page
70
70
  end
@@ -0,0 +1,267 @@
1
+ # Copyright (C) 2005 Thomas Leitner
2
+ #
3
+ # See LICENCE for details
4
+
5
+ require "xmlrpc/client"
6
+ require 'yaml'
7
+
8
+ module DevTools
9
+
10
+ module Freshmeat
11
+
12
+ class FreshmeatException < StandardError; end
13
+
14
+ # Describes a freshmeat project.
15
+ class Project
16
+
17
+ # The short name of the project
18
+ attr_reader :shortName
19
+
20
+ # The full name of the project
21
+ attr_reader :fullName
22
+
23
+ # The status of the project (alpha, beta, ...)
24
+ attr_reader :status
25
+
26
+ # The current version string of the project
27
+ attr_reader :version
28
+
29
+ def initialize( shortName, fullName, status, version )
30
+ @shortName, @fullName, @status, @version = shortName, fullName, status, version
31
+ end
32
+
33
+ def to_s
34
+ "Project: short name = #{@shortName}, full name = #{@fullName}, status = #{@status}, version = #{@version}"
35
+ end
36
+
37
+ end
38
+
39
+ # Provides information about a release.
40
+ class ReleaseInfo
41
+
42
+ # Version string of release
43
+ attr_reader :version
44
+
45
+ # Change list of release
46
+ attr_reader :changes
47
+
48
+ # Release focus
49
+ attr_reader :release_focus
50
+
51
+ # True, if hidden from frontpage
52
+ attr_reader :hidden
53
+
54
+ # Returns a new ReleaseInfo object
55
+ def initialize( version, changes, release_focus, hidden )
56
+ @version, @changes, @release_focus, @hidden = version, changes, release_focus, hidden
57
+ end
58
+
59
+ def to_s
60
+ "ReleaseInfo: version = #{@version}, release focus = #{ReleaseFocusID::NAMES[@release_focus]}, hidden = #{hidden}\nChanges = #{@changes}"
61
+ end
62
+
63
+ end
64
+
65
+ # Holds all information about a release which should be published.
66
+ class Release
67
+
68
+ #Project name to submit a release for
69
+ attr_accessor :project_name
70
+
71
+ #Branch name to submit a release for
72
+ attr_accessor :branch_name
73
+
74
+ #Version string of new release
75
+ attr_accessor :version
76
+
77
+ #Changes list, no HTML, character limit 600 chars
78
+ attr_accessor :changes
79
+
80
+ #Release focus ID of new release
81
+ attr_accessor :release_focus
82
+
83
+ #Set to 'Y' if release is to be hidden from frontpage, everything else does not hide it
84
+ attr_accessor :hide_from_frontpage
85
+
86
+ #Optional: Branch license
87
+ attr_accessor :license
88
+
89
+ #Optional: Homepage
90
+ attr_accessor :url_homepage
91
+
92
+ #Optional: Tar/GZ
93
+ attr_accessor :url_tgz
94
+
95
+ #Optional: Tar/BZ2
96
+ attr_accessor :url_bz2
97
+
98
+ #Optional: Zip
99
+ attr_accessor :url_zip
100
+
101
+ #Optional: Changelog
102
+ attr_accessor :url_changelog
103
+
104
+ #Optional: RPM package
105
+ attr_accessor :url_rpm
106
+
107
+ #Optional: Debian package
108
+ attr_accessor :url_deb
109
+
110
+ #Optional: OS X package
111
+ attr_accessor :url_osx
112
+
113
+ #Optional: BSD Ports URL
114
+ attr_accessor :url_bsdport
115
+
116
+ #Optional: Purchase
117
+ attr_accessor :url_purchase
118
+
119
+ #Optional: CVS tree (cvsweb)
120
+ attr_accessor :url_cvs
121
+
122
+ #Optional: Mailing list archive
123
+ attr_accessor :url_list
124
+
125
+ #Optional: Mirror site
126
+ attr_accessor :url_mirror
127
+
128
+ #Optional: Demo site
129
+ attr_accessor :url_demo
130
+
131
+ def self.from_data( data=nil )
132
+ YAML::load( data )
133
+ end
134
+
135
+ def to_rpc_data
136
+ rpc_data = {}
137
+ instance_variables.each do |iv|
138
+ rpc_data[iv[1..-1]] = eval( iv )
139
+ end
140
+ rpc_data
141
+ end
142
+
143
+ def to_yaml_type
144
+ "!thomasleitner,2005/FreshmeatRelease"
145
+ end
146
+
147
+ YAML::add_domain_type( 'thomasleitner,2005', 'FreshmeatRelease' ) do |type, val|
148
+ YAML::object_maker( Release, val )
149
+ end
150
+
151
+ end
152
+
153
+ # Provides constants for all release focus ids
154
+ module ReleaseFocusID
155
+ NA = 0
156
+ INITIAL_ANNOUNCEMENT = 1
157
+ DOCUMENTATION = 2
158
+ CODE_CLEANUP = 3
159
+ MINOR_FEATURES = 4
160
+ MAJOR_FEATURES = 5
161
+ MINOR_BUGFIXES = 6
162
+ MAJOR_BUGFIXES = 7
163
+ MINOR_SECFIXES = 8
164
+ MAJOR_SECFIXES = 9
165
+
166
+ NAMES = {0=>'N/A',
167
+ 1=>'Initial freshmeat announcement',
168
+ 2=>'Documentation',
169
+ 3=>'Code cleanup',
170
+ 4=>'Minor feature enhancements',
171
+ 5=>'Major feature enhancements',
172
+ 6=>'Minor bugfixes',
173
+ 7=>'Major bugfixes',
174
+ 8=>'Minor security fixes',
175
+ 9=>'Major security fixes'
176
+ }
177
+ end
178
+
179
+
180
+ # Provides access to the Freshmeat XML-RPC API via a nice interface.
181
+ # API reference at http://freshmeat.net/faq/view/49/
182
+ #
183
+ # Example:
184
+ #
185
+ # fm = DevTools::FreshmeatService.new( username, password )
186
+ # fm.get_project_list.each do |p|
187
+ # puts p
188
+ # branches = fm.get_branch_list( p.shortName )
189
+ # puts branches.inspect
190
+ # release = fm.get_release( p.shortName, branches[0], p.version )
191
+ # puts release
192
+ # end
193
+ # puts fm.logout
194
+ class FreshmeatService
195
+
196
+ # The URL for the Freshmeat RPC service
197
+ RPCURL = 'http://freshmeat.net/xmlrpc/'
198
+
199
+ # The major version of the freshmeat API with which this library works
200
+ API_VERSION_MAJOR = '1'
201
+
202
+ # The minor version of the freshmeat API with which this library works
203
+ API_VERSION_MINOR = '03'
204
+
205
+ # Use +username+ and +password+ to log into Freshmeat service.
206
+ def initialize( username, password )
207
+ @session = XMLRPC::Client.new2( RPCURL )
208
+ ret = @session.call( :login, :username=>username, :password=>password )
209
+ @sid = ret['SID']
210
+ major, minor = ret['API Version'].split( '.' )
211
+ if major != API_VERSION_MAJOR or minor < API_VERSION_MINOR
212
+ raise FreshmeatException, 'Incompatible API versions'
213
+ end
214
+ end
215
+
216
+ # Returns all available licenses
217
+ def self.get_licenses
218
+ XMLRPC::Client.new2( RPCURL ).call( :fetch_available_licenses )
219
+ end
220
+
221
+ # Returns all available release focus types
222
+ def self.get_release_focus_types
223
+ XMLRPC::Client.new2( RPCURL ).call( :fetch_available_release_foci )
224
+ end
225
+
226
+ # Returns an array of Project objects which are assigned to the logged in user
227
+ def get_project_list
228
+ ret = @session.call( :fetch_project_list, :SID=>@sid )
229
+ ret.collect! {|p| Project.new( p['projectname_short'], p['projectname_full'], p['project_status'], p['project_version'] ) }
230
+ end
231
+
232
+ # Returns an array of branch names for the project +project+.
233
+ def get_branch_list( project )
234
+ @session.call( :fetch_branch_list, :SID=>@sid, :project_name=>project )
235
+ end
236
+
237
+ # Returns a ReleaseInfo object which has the information about the requested release.
238
+ def get_release( project, branch, version )
239
+ ret = @session.call( :fetch_release, :SID=>@sid, :project_name=>project, :branch_name=>branch, :version=>version )
240
+ releaseFocus = ret['release_focus'].split( ' - ' )[0].to_i
241
+ hidden = (ret['hide_from_frontpage'] == 'Y' )
242
+ ReleaseInfo.new( ret['version'], ret['changes'], releaseFocus, hidden )
243
+ end
244
+
245
+ # Publishes a new release of a project. The parameter +release+ has to be a Release object!
246
+ def publish_release( release )
247
+ ret = @session.call( :publish_release, {:SID=>@sid}.update( release.to_rpc_data ) )
248
+ ret['OK'] == 'submission successful'
249
+ end
250
+
251
+ # Withdraws the specified release.
252
+ def withdraw_release( project, branch, version )
253
+ ret = @session.call( :withdraw_release, :SID=>@sid, :project_name=>project, :branch_name=>branch, :version=>version )
254
+ ret['OK'] == 'Withdraw successful.'
255
+ end
256
+
257
+ # Logs out from the Freshmeat service.
258
+ def logout
259
+ ok, ret = @session.call2( :logout, :SID=>@sid )
260
+ ok && ret['OK'] == 'Logout successful.'
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+
267
+ end