reap 6.0.2 → 9.2.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.
Files changed (174) hide show
  1. data/CHANGES +6 -0
  2. data/COPYING +666 -395
  3. data/MANIFEST +139 -0
  4. data/NOTES +12 -0
  5. data/README +19 -163
  6. data/bin/reap-announce +7 -0
  7. data/bin/reap-check-load +8 -0
  8. data/bin/reap-check-syntax +7 -0
  9. data/bin/reap-clean +7 -0
  10. data/bin/reap-clobber +7 -0
  11. data/bin/reap-doc +7 -0
  12. data/bin/reap-doc-rdoc +7 -0
  13. data/bin/reap-doc-ri +7 -0
  14. data/bin/reap-init +69 -0
  15. data/bin/reap-inspect +7 -0
  16. data/bin/reap-install +8 -0
  17. data/bin/reap-install-gem +8 -0
  18. data/bin/reap-log +7 -0
  19. data/bin/reap-log-changes +8 -0
  20. data/bin/reap-log-notes +8 -0
  21. data/bin/reap-make +7 -0
  22. data/bin/reap-make-clean +7 -0
  23. data/bin/reap-make-distclean +7 -0
  24. data/bin/reap-make-extconf +7 -0
  25. data/bin/reap-make-static +8 -0
  26. data/bin/reap-package +8 -0
  27. data/bin/reap-package-gem +7 -0
  28. data/bin/reap-package-tgz +7 -0
  29. data/bin/reap-package-zip +7 -0
  30. data/bin/reap-prepare +7 -0
  31. data/bin/reap-publish +7 -0
  32. data/bin/reap-release +7 -0
  33. data/bin/reap-scaffold +8 -0
  34. data/bin/reap-scm-branch +7 -0
  35. data/bin/reap-scm-tag +7 -0
  36. data/bin/reap-spec +7 -0
  37. data/bin/reap-spec-doc +8 -0
  38. data/bin/reap-stamp +7 -0
  39. data/bin/reap-stats +7 -0
  40. data/bin/reap-test +7 -0
  41. data/bin/reap-test-cross +7 -0
  42. data/bin/reap-test-load +9 -0
  43. data/bin/reap-test-solo +7 -0
  44. data/bin/reap-uninstall +7 -0
  45. data/bin/reap-uninstall-gem +7 -0
  46. data/data/reap/base/CHANGES +19 -0
  47. data/data/reap/base/COPYING +674 -0
  48. data/data/reap/base/NOTES +12 -0
  49. data/data/reap/base/README +8 -0
  50. data/data/reap/build/rake/Rakefile +130 -0
  51. data/{forge/reference/installers → data/reap/build/rake}/setup.rb +155 -97
  52. data/data/reap/build/rake-lite/install.rb +79 -0
  53. data/data/reap/build/tasks/task/rdoc +211 -0
  54. data/data/reap/{setup.rb → build/tasks/task/setup} +248 -200
  55. data/data/reap/build/tasks/task/test +38 -0
  56. data/data/reap/init/meta/project.yaml +21 -0
  57. data/demo/README +15 -0
  58. data/demo/lib/foo/foo.rb +7 -0
  59. data/demo/meta/VERSION +1 -0
  60. data/demo/meta/project.yaml +21 -0
  61. data/lib/reap/application.rb +80 -0
  62. data/lib/reap/default.yaml +77 -0
  63. data/lib/reap/extensions/array.rb +50 -0
  64. data/lib/reap/extensions/hash.rb +63 -0
  65. data/lib/reap/extensions/string.rb +31 -0
  66. data/lib/reap/extensions.rb +6 -0
  67. data/lib/reap/iobject.rb +264 -0
  68. data/lib/reap/manager/announce.rb +193 -0
  69. data/lib/reap/manager/check.rb +109 -0
  70. data/lib/reap/manager/clean.rb +58 -0
  71. data/lib/reap/manager/gem.rb +200 -0
  72. data/lib/reap/manager/html.rb +69 -0
  73. data/lib/reap/manager/log.rb +214 -0
  74. data/lib/reap/manager/make.rb +109 -0
  75. data/lib/reap/manager/pack.rb +232 -0
  76. data/lib/reap/manager/publish.rb +72 -0
  77. data/lib/reap/manager/rdoc.rb +194 -0
  78. data/lib/reap/manager/release.rb +78 -0
  79. data/lib/reap/manager/rubyforge.rb +44 -0
  80. data/lib/reap/manager/scaffold.rb +138 -0
  81. data/lib/reap/manager/scm.rb +66 -0
  82. data/lib/reap/manager/site.rb +27 -0
  83. data/lib/reap/manager/spec.rb +94 -0
  84. data/lib/reap/manager/stats.rb +145 -0
  85. data/lib/reap/manager/svn.rb +76 -0
  86. data/lib/reap/manager/test.rb +389 -0
  87. data/lib/reap/manager.rb +74 -0
  88. data/lib/reap/metadata.rb +603 -0
  89. data/lib/reap/project.rb +165 -0
  90. data/lib/reap/settings.rb +67 -0
  91. data/lib/reap/systems/rubyforge.rb +737 -0
  92. data/lib/reap/systems/subversion.rb +333 -0
  93. data/lib/reap/utilities/fileutils.rb +304 -0
  94. data/lib/reap/utilities/netutils.rb +221 -0
  95. data/lib/reap/utilities/setuputils.rb +124 -0
  96. data/lib/reap/utilities/shellutils.rb +175 -0
  97. data/log/Changelog.txt +107 -0
  98. data/log/FIXME.txt +25 -0
  99. data/log/TODO.txt +72 -0
  100. data/meta/VERSION +1 -0
  101. data/meta/description +4 -0
  102. data/meta/project.yaml +20 -0
  103. data/task/man +14 -0
  104. data/task/rdoc +20 -0
  105. data/{data/reap/scaffold/standard/setup.rb → task/setup} +248 -200
  106. metadata +225 -153
  107. data/ProjectInfo +0 -105
  108. data/bin/reap +0 -5
  109. data/bin/rubytest +0 -5
  110. data/data/reap/install.rb +0 -62
  111. data/data/reap/scaffold/standard/COPYING +0 -403
  112. data/data/reap/scaffold/standard/ChangeLog +0 -0
  113. data/data/reap/scaffold/standard/INSTALL +0 -14
  114. data/data/reap/scaffold/standard/ProjectInfo +0 -77
  115. data/data/reap/scaffold/standard/README +0 -3
  116. data/data/reap/scaffold/standard/Rakefile +0 -10
  117. data/data/reap/scaffold/standard/TODO +0 -0
  118. data/data/reap/scaffold/subversion/trunk/COPYING +0 -403
  119. data/data/reap/scaffold/subversion/trunk/ChangeLog +0 -0
  120. data/data/reap/scaffold/subversion/trunk/INSTALL +0 -14
  121. data/data/reap/scaffold/subversion/trunk/ProjectInfo +0 -76
  122. data/data/reap/scaffold/subversion/trunk/README +0 -3
  123. data/data/reap/scaffold/subversion/trunk/Rakefile +0 -10
  124. data/data/reap/scaffold/subversion/trunk/TODO +0 -0
  125. data/data/reap/scaffold/subversion/trunk/setup.rb +0 -1568
  126. data/forge/ProjectInfo +0 -38
  127. data/forge/ProjectInfo.rb +0 -76
  128. data/forge/TODO +0 -10
  129. data/forge/installer.rb +0 -250
  130. data/forge/reference/Rakefile +0 -124
  131. data/forge/reference/Rakefile.htm +0 -69
  132. data/forge/reference/aRakefile +0 -60
  133. data/forge/reference/compositepublisher.rb +0 -24
  134. data/forge/reference/ftptools.rb +0 -139
  135. data/forge/reference/installers/package.rb +0 -629
  136. data/forge/reference/license-each.rb +0 -85
  137. data/forge/reference/publisher.rb +0 -75
  138. data/forge/reference/rubyforge.rb +0 -247
  139. data/forge/reference/rubyforgepublisher.rb +0 -18
  140. data/forge/reference/sshpublisher.rb +0 -47
  141. data/forge/reference/suby-cvs.rb +0 -46
  142. data/forge/scaffold.rb +0 -126
  143. data/forge/unit_runner/README +0 -6
  144. data/forge/unit_runner/commentrunner.rb +0 -62
  145. data/forge/unit_runner/cunit.rb +0 -17
  146. data/forge/unit_runner/forkedrunner.rb +0 -91
  147. data/forge/unit_runner/sample.rb +0 -16
  148. data/lib/reap/bin/reap.rb +0 -230
  149. data/lib/reap/bin/rubytest.rb +0 -53
  150. data/lib/reap/class/announce.rb +0 -220
  151. data/lib/reap/class/doap.rb +0 -80
  152. data/lib/reap/class/extest.rb +0 -146
  153. data/lib/reap/class/filer.rb +0 -62
  154. data/lib/reap/class/manifest.rb +0 -68
  155. data/lib/reap/class/package.rb +0 -576
  156. data/lib/reap/class/publish.rb +0 -152
  157. data/lib/reap/class/rdoc.rb +0 -123
  158. data/lib/reap/class/test.rb +0 -264
  159. data/lib/reap/projectinfo.rb +0 -208
  160. data/lib/reap/rake.rb +0 -42
  161. data/lib/reap/reap.rb +0 -89
  162. data/lib/reap/tasks.rb +0 -756
  163. data/lib/reap/taskutils.rb +0 -122
  164. data/note/LATEST +0 -44
  165. data/note/doap.xml +0 -28
  166. data/note/history/Rakefile-0.1 +0 -308
  167. data/sample/ProjectInfo +0 -96
  168. data/sample/Rakefile +0 -9
  169. data/sample/Reapfile +0 -11
  170. data/sample/task/demo.rb +0 -15
  171. data/setup.rb +0 -1568
  172. data/web/ProjectInfo.html +0 -75
  173. data/web/images/grape.jpg +0 -0
  174. data/web/index.html +0 -312
@@ -0,0 +1,737 @@
1
+ require 'enumerator'
2
+ require 'fileutils'
3
+ require 'open-uri'
4
+ require 'openssl'
5
+ require 'ostruct'
6
+ require 'httpclient'
7
+ require 'tmpdir'
8
+
9
+ require 'facets/hash/rekey'
10
+ require 'facets/kernel/ask'
11
+
12
+ #require 'reap/utilities/uploadutils'
13
+
14
+ # def self.factory(name)
15
+ # ["proutils/icli/#{name.downcase}.rb"].each{ |x| require x } # only did it this way to shut rdoc up!
16
+ # ICli::const_get(name.capitalize)
17
+ # end
18
+
19
+ module Reap
20
+
21
+ # Interface with the RubyForge hosting service.
22
+ # Currently supports functions:
23
+ #
24
+ # * release - Upload release packages.
25
+ # * publish - Upload website files.
26
+ # * announce - Post news announcement.
27
+ # * touch - Test connection.
28
+
29
+ class Rubyforge
30
+
31
+ include FileUtils
32
+
33
+ #HOME = ENV["HOME"] || ENV["HOMEPATH"] || File.expand_path("~")
34
+
35
+ COOKIEJAR = File::join(Dir.tmpdir, 'reap', 'cookie.dat')
36
+
37
+ REPORT = /<h\d><span style="color:red">(.*?)<\/span><\/h\d>/
38
+
39
+ # URI = http:// + domain name
40
+ # TODO Deal with https, and possible other protocols too.
41
+ attr :uri
42
+
43
+ # Many sites will require logging in so these are available by
44
+ # for use even if they arn't used.
45
+ attr :username
46
+ attr :password
47
+
48
+ # Domain name of host. Must be overriden by adapter.
49
+ def domain
50
+ raise "Missing Domain"
51
+ end
52
+
53
+ # New RubyForge tasks.
54
+ def initialize(options)
55
+ options = options.dup.rekey
56
+
57
+ @dryrun = options[:dryrun]
58
+ @trace = options[:trace]
59
+
60
+ #@domain = options[:domain] || default_domain
61
+ @uri = URI.parse("http://" + domain)
62
+
63
+ @username = options[:username] || ENV['RUBYFORGE_USERNAME']
64
+ @password = options[:password]
65
+
66
+ mkdir_p(File.dirname(COOKIEJAR))
67
+ @cookie_jar = COOKIEJAR
68
+
69
+ @project = options[:project]
70
+ @group_id = options[:group_id] || options[:groupid]
71
+
72
+ @package_ids = {}
73
+ @release_ids = {}
74
+ @file_ids = {}
75
+
76
+ load_project
77
+ end
78
+
79
+ def dryrun?
80
+ @dryrun
81
+ end
82
+
83
+ def trace?
84
+ @trace
85
+ end
86
+
87
+ public
88
+
89
+ #
90
+ def domain
91
+ "rubyforge.org"
92
+ end
93
+
94
+ # Website location on server.
95
+ def siteroot
96
+ "/var/www/gforge-projects"
97
+ end
98
+
99
+ # What commands does this host support.
100
+
101
+ def commands
102
+ %w{ touch release publish post }
103
+ end
104
+
105
+ # Project name.
106
+ attr_accessor :project
107
+
108
+ # Project's group id number.
109
+ attr_accessor :group_id
110
+
111
+ # Username to use for project account.
112
+ attr_accessor :username
113
+
114
+ # Login to website.
115
+
116
+ def login # :yield:
117
+ page = @uri + "/account/login.php"
118
+ page.scheme = 'https'
119
+ page = URI.parse(page.to_s) # set SSL port correctly
120
+
121
+ form = {
122
+ "return_to" => "",
123
+ "form_loginname" => username,
124
+ "form_pw" => password,
125
+ "login" => "Login with SSL"
126
+ }
127
+ html = http_post(page, form)
128
+
129
+ unless html[/Personal Page/]
130
+ puts "Login failed."
131
+ re1 = Regexp.escape(%{<h2 style="color:red">})
132
+ re2 = Regexp.escape(%{</h2>})
133
+ html[/#{re1}(.*?)#{re2}/]
134
+ raise $1
135
+ end
136
+
137
+ if block_given?
138
+ begin
139
+ yield
140
+ ensure
141
+ logout
142
+ end
143
+ end
144
+ end
145
+
146
+ # Logout of website.
147
+
148
+ def logout
149
+ page = "/account/logout.php"
150
+ form = {}
151
+ http_post(page, form)
152
+ end
153
+
154
+ # Touch base with server -- login and logout.
155
+
156
+ def touch(options={})
157
+ login
158
+ puts "Group ID: #{group_id}"
159
+ puts "Login/Logout successful."
160
+ logout
161
+ end
162
+
163
+ # Upload release packages to hosting service.
164
+ #
165
+ # This task releases files to RubyForge --it should work with other
166
+ # GForge instaces or SourceForge clones too.
167
+ #
168
+ # While defaults are nice, you may want a little more control. You can
169
+ # specify additional attributes:
170
+ #
171
+ # files package files to release.
172
+ # exclude Package formats to exclude from files.
173
+ # (from those created by pack)
174
+ # project Project name on host.
175
+ # package Package to which this release belongs (defaults to project)
176
+ # release Release name (default is version number)
177
+ # version Version of release
178
+ # date Date of release (defaults to Time.now)
179
+ # processor Processor/Architecture (any, i386, PPC, etc.)
180
+ # is_public Public release? (defualts to true)
181
+ # changelog Change log file
182
+ # notelog Release notes file
183
+ #
184
+ # The release option can be a template by using %s in the
185
+ # string. The version number of your project will be sub'd
186
+ # in for the %s. This saves you from having to update
187
+ # the release name before every release.
188
+ #
189
+ #--
190
+ # What about releasing a pacman PKGBUILD?
191
+ #++
192
+
193
+ def release(options)
194
+ options = options.rekey
195
+
196
+ version = options[:version]
197
+ changelog = options[:changelog]
198
+ notelog = options[:notelog]
199
+
200
+ project = options[:project] || @project
201
+ package = options[:package] || project
202
+ release = options[:release] || version
203
+ name = options[:name] || package
204
+ files = options[:file] || []
205
+ date = options[:date] || Time::now.strftime('%Y-%m-%d %H:%M')
206
+ processor = options[:processor] || 'Any'
207
+ store = options[:store] || 'pkg'
208
+
209
+ is_public = options[:is_public].nil? ? true : options[:is_public]
210
+
211
+ raise ArgumentError, "missing group_id" unless group_id
212
+ raise ArgumentError, "missing project" unless project
213
+ raise ArgumentError, "missing package" unless package
214
+ raise ArgumentError, "missing release" unless release
215
+
216
+ if files.empty?
217
+ files = Dir[File.join(store, '*')].select do |file|
218
+ /#{version}[.]/ =~ file
219
+ end
220
+ #files = Dir.glob(File.join(store,"#{name}-#{version}*"))
221
+ end
222
+
223
+ files = files.select{ |f| File.file?(f) }
224
+
225
+ abort "No package files." if files.empty?
226
+
227
+ files.each do |file|
228
+ abort "Not a file -- #{file}" unless File.exist?(file)
229
+ puts "Release file: #{file}"
230
+ end
231
+
232
+ # which package types
233
+ #rtypes = [ 'tgz', 'tbz', 'tar.gz', 'tar.bz2', 'deb', 'gem', 'ebuild', 'zip' ]
234
+ #rtypes -= exclude
235
+ #rtypes = rtypes.collect{ |rt| Regexp.escape( rt ) }
236
+ #re_rtypes = Regexp.new('[.](' << rtypes.join('|') << ')$')
237
+
238
+ puts "Releasing #{package} #{release}..." #unless options['quiet']
239
+
240
+ login do
241
+
242
+ unless package_id = package?(package)
243
+ #unless options['force']
244
+ q = "Package '#{package}' does not exist. Create?"
245
+ a = ask(q, 'yN')
246
+ abort "Task canceled." unless ['y', 'yes', 'okay'].include?(a.downcase)
247
+ #end
248
+ puts "Creating package #{package}..."
249
+ abort "Cannot continue in dry-run mode." if dryrun?
250
+ create_package(package, is_public)
251
+ unless package_id = package?(package)
252
+ raise "Package creation failed."
253
+ end
254
+ end
255
+ if release_id = release?(release, package_id)
256
+ #unless options[:force]
257
+ q = "Release #{release} already exists. Re-release?"
258
+ a = ask(q, 'yN')
259
+ abort "Task canceled." unless ['y', 'yes', 'okay'].include?(a.downcase)
260
+ #puts "Use -f option to force re-release."
261
+ #return
262
+ #end
263
+ files.each do |file|
264
+ fname = File.basename(file)
265
+ if file_id = file?(fname, package)
266
+ puts "Removing file #{fname}..."
267
+ remove_file(file_id, release_id, package_id) unless dryrun?
268
+ end
269
+ puts "Adding file #{fname}..."
270
+ add_file(file, release_id, package_id, processor) unless dryrun?
271
+ end
272
+ else
273
+ puts "Adding release #{release}..."
274
+ unless dryrun?
275
+ add_release(release, package_id, files,
276
+ :processor => processor,
277
+ :release_date => date,
278
+ :release_changes => changelog,
279
+ :release_notes => notelog,
280
+ :preformatted => '1'
281
+ )
282
+ unless release_id = release?(release, package_id)
283
+ raise "Release creation failed."
284
+ end
285
+ end
286
+ #files.each do |file|
287
+ # puts "Added file #{File.basename(file)}."
288
+ #end
289
+ end
290
+ end
291
+ puts "Release complete!"
292
+ end
293
+
294
+ # #
295
+ # # Publish documents to website.
296
+ # #
297
+ # # TODO Fix publish method for Rubyforge tool.
298
+ #
299
+ # def publish(options)
300
+ # options = options.rekey
301
+ #
302
+ # #domain = options[:domain] || DOMAIN
303
+ # root = File.join(siteroot, project)
304
+ # root = File.join(root, options[:root]) if options[:root]
305
+ #
306
+ # options.update(
307
+ # :host => domain,
308
+ # :root => root
309
+ # )
310
+ #
311
+ # UploadUtils.rsync(options)
312
+ # end
313
+
314
+ # Submit a news item.
315
+
316
+ def post(options)
317
+ options = options.rekey
318
+
319
+ if file = options[:file]
320
+ text = File.read(file).strip
321
+ i = text.index("\n")
322
+ subject = text[0...i].strip
323
+ message = text[i..-1].strip
324
+ else
325
+ subject = options[:subject]
326
+ message = options[:message] || options[:body]
327
+ end
328
+
329
+ if dryrun?
330
+ puts "\nSUBJECT: #{subject}"
331
+ puts "\n#{message}\n\n"
332
+ else
333
+ post_news(subject, message)
334
+ puts "News item posted!"
335
+ end
336
+ end
337
+
338
+
339
+ private
340
+
341
+ # HTTP POST transaction.
342
+
343
+ def http_post(page, form, extheader={})
344
+ client = HTTPClient::new ENV["HTTP_PROXY"]
345
+ client.debug_dev = STDERR if ENV["ICLI_DEBUG"] || ENV["DEBUG"] || $DEBUG
346
+ client.set_cookie_store(@cookie_jar)
347
+ client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
348
+
349
+ # HACK to fix http-client redirect bug/feature
350
+ client.redirect_uri_callback = lambda do |uri, res|
351
+ page = res.header['location'].first
352
+ page =~ %r/http/ ? page : @uri + page
353
+ end
354
+
355
+ uri = @uri + page
356
+ if $DEBUG then
357
+ puts "POST #{uri.inspect}"
358
+ puts "#{form.inspect}"
359
+ puts "#{extheader.inspect}" unless extheader.empty?
360
+ puts
361
+ end
362
+
363
+ response = client.post_content uri, form, extheader
364
+
365
+ if response[REPORT]
366
+ puts "(" + $1 + ")"
367
+ end
368
+
369
+ client.save_cookie_store
370
+
371
+ return response
372
+ end
373
+
374
+ # Loads information for project: group_id, package_ids and release_ids.
375
+
376
+ def load_project
377
+ puts "Project: #{project}"
378
+
379
+ html = URI.parse("http://#{domain}/projects/#{project}/index.html").read
380
+
381
+ group_id = html[/(frs|tracker)\/\?group_id=\d+/][/\d+/].to_i
382
+ @group_id = group_id
383
+
384
+ if $DEBUG
385
+ puts "GROUP_ID = #{group_id}"
386
+ end
387
+
388
+ html = URI.parse("http://rubyforge.org/frs/?group_id=#{group_id}").read
389
+
390
+ package = nil
391
+ html.scan(/<h3>[^<]+|release_id=\d+">[^>]+|filemodule_id=\d+/).each do |s|
392
+ case s
393
+ when /<h3>([^<]+)/ then
394
+ package = $1.strip
395
+ when /filemodule_id=(\d+)/ then
396
+ @package_ids[package] = $1.to_i
397
+ when /release_id=(\d+)">([^<]+)/ then
398
+ package_id = @package_ids[package]
399
+ @release_ids[[package_id,$2]] = $1.to_i
400
+ end
401
+ end
402
+
403
+ if $DEBUG
404
+ p @package_ids, @release_ids
405
+ end
406
+ end
407
+
408
+ # Returns password. If not already set, will ask for it.
409
+
410
+ def password
411
+ @password ||= ENV['RUBYFORGE_PASSWORD']
412
+ @password ||= (
413
+ print "Password for #{username}: "
414
+ until inp = $stdin.gets ; sleep 1 ; end ; puts
415
+ inp.strip
416
+ )
417
+ end
418
+
419
+ # Package exists? Returns package-id number.
420
+
421
+ def package?(package_name)
422
+ id = @package_ids[package_name]
423
+ return id if id
424
+
425
+ package_id = nil
426
+
427
+ page = "/frs/"
428
+
429
+ form = {
430
+ "group_id" => group_id
431
+ }
432
+ scrape = http_post(page, form)
433
+
434
+ restr = ''
435
+ restr << Regexp.escape( package_name )
436
+ restr << '\s*'
437
+ restr << Regexp.escape( %{<a href="/frs/monitor.php?filemodule_id=} )
438
+ restr << '(\d+)'
439
+ restr << Regexp.escape( %{&group_id=#{group_id}} )
440
+ re = Regexp.new( restr )
441
+
442
+ md = re.match( scrape )
443
+ if md
444
+ package_id = md[1]
445
+ end
446
+
447
+ @package_ids[package_name] = package_id
448
+ end
449
+
450
+ # Create a new package.
451
+
452
+ def create_package( package_name, is_public=true )
453
+ page = "/frs/admin/index.php"
454
+
455
+ form = {
456
+ "func" => "add_package",
457
+ "group_id" => group_id,
458
+ "package_name" => package_name,
459
+ "is_public" => (is_public ? 1 : 0),
460
+ "submit" => "Create This Package"
461
+ }
462
+
463
+ http_post(page, form)
464
+ end
465
+
466
+ # Delete package.
467
+
468
+ def delete_package(package_id)
469
+ page = "/frs/admin/index.php"
470
+
471
+ form = {
472
+ "func" => "delete_package",
473
+ "group_id" => group_id,
474
+ "package_id" => package_id,
475
+ "sure" => "1",
476
+ "really_sure" => "1",
477
+ "submit" => "Delete",
478
+ }
479
+
480
+ http_post(page, form)
481
+ end
482
+
483
+ # Release exits? Returns release-id number.
484
+
485
+ def release?(release_name, package_id)
486
+ id = @release_ids[[release_name,package_id]]
487
+ return id if id
488
+
489
+ release_id = nil
490
+
491
+ page = "/frs/admin/showreleases.php"
492
+
493
+ form = {
494
+ "package_id" => package_id,
495
+ "group_id" => group_id
496
+ }
497
+ scrape = http_post( page, form )
498
+
499
+ restr = ''
500
+ restr << Regexp.escape( %{"editrelease.php?group_id=#{group_id}} )
501
+ restr << Regexp.escape( %{&amp;package_id=#{package_id}} )
502
+ restr << Regexp.escape( %{&amp;release_id=} )
503
+ restr << '(\d+)'
504
+ restr << Regexp.escape( %{">#{release_name}} )
505
+ re = Regexp.new( restr )
506
+
507
+ md = re.match( scrape )
508
+ if md
509
+ release_id = md[1]
510
+ end
511
+
512
+ @release_ids[[release_name,package_id]] = release_id
513
+ end
514
+
515
+ # Add a new release.
516
+
517
+ def add_release(release_name, package_id, *files)
518
+ page = "/frs/admin/qrs.php"
519
+
520
+ options = (Hash===files.last ? files.pop : {}).rekey
521
+ files = files.flatten
522
+
523
+ processor = options[:processor]
524
+ release_date = options[:release_date]
525
+ release_changes = options[:release_changes]
526
+ release_notes = options[:release_notes]
527
+
528
+ release_date ||= Time::now.strftime("%Y-%m-%d %H:%M")
529
+
530
+ file = files.shift
531
+ puts "Adding file #{File.basename(file)}..."
532
+ userfile = open(file, 'rb')
533
+
534
+ type_id = userfile.path[%r|\.[^\./]+$|]
535
+ type_id = FILETYPES[type_id]
536
+ processor_id = PROCESSORS[processor.downcase]
537
+
538
+ # TODO IS THIS WORKING?
539
+ release_notes = IO::read(release_notes) if release_notes and test(?f, release_notes)
540
+ release_changes = IO::read(release_changes) if release_changes and test(?f, release_changes)
541
+
542
+ preformatted = '1'
543
+
544
+ form = {
545
+ "group_id" => group_id,
546
+ "package_id" => package_id,
547
+ "release_name" => release_name,
548
+ "release_date" => release_date,
549
+ "type_id" => type_id,
550
+ "processor_id" => processor_id,
551
+ "release_notes" => release_notes,
552
+ "release_changes" => release_changes,
553
+ "preformatted" => preformatted,
554
+ "userfile" => userfile,
555
+ "submit" => "Release File"
556
+ }
557
+
558
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
559
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
560
+
561
+ html = http_post(page, form, 'content-type' => boundary)
562
+
563
+ release_id = html[/release_id=\d+/][/\d+/].to_i
564
+ puts "RELEASE ID = #{release_id}" if $DEBUG
565
+
566
+ files.each do |file|
567
+ puts "Adding file #{File.basename(file)}..."
568
+ add_file(file, release_id, package_id, processor)
569
+ end
570
+
571
+ release_id
572
+ end
573
+
574
+ # File exists?
575
+ #
576
+ # NOTE this is a bit fragile. If two releases have the same exact
577
+ # file name in them there could be a problem --that's probably not
578
+ # likely, but I can't yet rule it out.
579
+ #
580
+ # TODO Remove package argument, it is no longer needed.
581
+
582
+ def file?(file, package)
583
+ id = @file_ids[[file, package]]
584
+ return id if id
585
+
586
+ file_id = nil
587
+
588
+ page = "/frs/"
589
+
590
+ form = {
591
+ "group_id" => group_id
592
+ }
593
+ scrape = http_post(page, form)
594
+
595
+ restr = ''
596
+ #restr << Regexp.escape( package )
597
+ #restr << '\s*'
598
+ restr << Regexp.escape( %{<a href="/frs/download.php/} )
599
+ restr << '(\d+)'
600
+ restr << Regexp.escape( %{/#{file}} )
601
+ re = Regexp.new(restr)
602
+
603
+ md = re.match(scrape)
604
+ if md
605
+ file_id = md[1]
606
+ end
607
+
608
+ @file_ids[[file, package]] = file_id
609
+ end
610
+
611
+ # Remove file from release.
612
+
613
+ def remove_file(file_id, release_id, package_id)
614
+ page="/frs/admin/editrelease.php"
615
+
616
+ form = {
617
+ "group_id" => group_id,
618
+ "package_id" => package_id,
619
+ "release_id" => release_id,
620
+ "file_id" => file_id,
621
+ "step3" => "Delete File",
622
+ "im_sure" => '1',
623
+ "submit" => "Delete File "
624
+ }
625
+
626
+ http_post(page, form)
627
+ end
628
+
629
+ #
630
+ # Add file to release.
631
+ #
632
+
633
+ def add_file(file, release_id, package_id, processor=nil)
634
+ page = '/frs/admin/editrelease.php'
635
+
636
+ userfile = open file, 'rb'
637
+
638
+ type_id = userfile.path[%r|\.[^\./]+$|]
639
+ type_id = FILETYPES[type_id]
640
+ processor_id = PROCESSORS[processor.downcase]
641
+
642
+ form = {
643
+ "step2" => '1',
644
+ "group_id" => group_id,
645
+ "package_id" => package_id,
646
+ "release_id" => release_id,
647
+ "userfile" => userfile,
648
+ "type_id" => type_id,
649
+ "processor_id" => processor_id,
650
+ "submit" => "Add This File"
651
+ }
652
+
653
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
654
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
655
+
656
+ http_post(page, form, 'content-type' => boundary)
657
+ end
658
+
659
+ # Posts news item to +group_id+ (can be name) with +subject+ and +body+
660
+
661
+ def post_news(subject, body)
662
+ page = "/news/submit.php"
663
+
664
+ form = {
665
+ "group_id" => group_id,
666
+ "post_changes" => "y",
667
+ "summary" => subject,
668
+ "details" => body,
669
+ "submit" => "Submit"
670
+ }
671
+
672
+ http_post(page, form)
673
+ end
674
+
675
+ # Constant for file types accepted by Rubyforge
676
+
677
+ FILETYPES = {
678
+ ".deb" => 1000,
679
+ ".rpm" => 2000,
680
+ ".zip" => 3000,
681
+ ".bz2" => 3100,
682
+ ".gz" => 3110,
683
+ ".src.zip" => 5000,
684
+ ".src.bz2" => 5010,
685
+ ".src.tar.bz2" => 5010,
686
+ ".src.gz" => 5020,
687
+ ".src.tar.gz" => 5020,
688
+ ".src.rpm" => 5100,
689
+ ".src" => 5900,
690
+ ".jpg" => 8000,
691
+ ".txt" => 8100,
692
+ ".text" => 8100,
693
+ ".htm" => 8200,
694
+ ".html" => 8200,
695
+ ".pdf" => 8300,
696
+ ".oth" => 9999,
697
+ ".ebuild" => 1300,
698
+ ".exe" => 1100,
699
+ ".dmg" => 1200,
700
+ ".tar.gz" => 3110,
701
+ ".tgz" => 3110,
702
+ ".gem" => 1400,
703
+ ".pgp" => 8150,
704
+ ".sig" => 8150
705
+ }
706
+
707
+ # Constant for processor types accepted by Rubyforge
708
+
709
+ PROCESSORS = {
710
+ "i386" => 1000,
711
+ "IA64" => 6000,
712
+ "Alpha" => 7000,
713
+ "Any" => 8000,
714
+ "PPC" => 2000,
715
+ "MIPS" => 3000,
716
+ "Sparc" => 4000,
717
+ "UltraSparc" => 5000,
718
+ "Other" => 9999,
719
+
720
+ "i386" => 1000,
721
+ "ia64" => 6000,
722
+ "alpha" => 7000,
723
+ "any" => 8000,
724
+ "ppc" => 2000,
725
+ "mips" => 3000,
726
+ "sparc" => 4000,
727
+ "ultrasparc" => 5000,
728
+ "other" => 9999,
729
+
730
+ "all" => 8000,
731
+ nil => 8000
732
+ }
733
+
734
+ end
735
+
736
+ end
737
+