rhype 0.1.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.
@@ -0,0 +1,876 @@
1
+ require 'rhype/hosts/gforge'
2
+
3
+ module RHype
4
+
5
+ #def rubyforge(options={})
6
+ # opts = config.rubyforge || {}
7
+ # opts = opts.update(options)
8
+ # Rubyforge.new(self, opts)
9
+ #end
10
+
11
+ # = Rubyforge
12
+ #
13
+ # Interface with the RubyForge hosting service.
14
+ # Supports the following tasks:
15
+ #
16
+ # * release - Upload release packages
17
+ # * publish - Publish website
18
+ # * announce - Post news announcement
19
+ # * touch - Test connection
20
+
21
+ class Rubyforge < GForge
22
+
23
+ #register('rubyforge', 'rubyforge.org')
24
+ #service_action :publish => :deploy
25
+ #service_action :release => :deploy
26
+ #service_action :announce => :deploy
27
+
28
+ #service_action :touch
29
+
30
+ #HOME = ENV["HOME"] || ENV["HOMEPATH"] || File.expand_path("~")
31
+ DOMAIN = "rubyforge.org"
32
+ COOKIEJAR = File::join(Dir.tmpdir, 'reap', 'cookie.dat')
33
+ REPORT = /<h\d><span style="color:red">(.*?)<\/span><\/h\d>/
34
+
35
+ # Project unixname.
36
+ attr_accessor :unixname
37
+
38
+ # Project name.
39
+ attr_accessor :version
40
+
41
+ # Project's group id number.
42
+ attr_accessor :group_id
43
+
44
+ alias_accessor :group , :group_id
45
+ alias_accessor :groupid, :group_id
46
+
47
+ # Username for project account.
48
+ attr_accessor :username
49
+
50
+ # Password for project account.
51
+ attr_accessor :password
52
+
53
+ #
54
+ attr_accessor :dryrun
55
+
56
+ #
57
+ attr_accessor :quiet
58
+
59
+ #
60
+ attr_accessor :verbose
61
+
62
+ #
63
+ #attr_accessor :domain
64
+
65
+ private
66
+
67
+ def initialize(options)
68
+ #@unixname = unixname
69
+ #@version = metadata.version
70
+ @username = ENV['RUBYFORGE_USERNAME']
71
+ @password = ENV['RUBYFORGE_PASSWORD']
72
+ @domain = DOMAIN
73
+
74
+ super(options)
75
+
76
+ @package_ids = {}
77
+ @release_ids = {}
78
+ @file_ids = {}
79
+ end
80
+
81
+ public
82
+
83
+ def dryrun? ; @dryrun ; end
84
+ def quiet? ; @quiet ; end
85
+ def verbose? ; @verbose ; end
86
+
87
+ # New RubyForge object.
88
+ #
89
+ #def initialize(project, options={})
90
+ #end
91
+
92
+ # URI = http:// + domain name
93
+ #
94
+ # TODO: Deal with https, and possible other protocols too.
95
+
96
+ def uri
97
+ @uri ||= URI.parse("http://" + domain)
98
+ end
99
+
100
+ #
101
+
102
+ def cookie_jar
103
+ COOKIEJAR
104
+ end
105
+
106
+ public
107
+
108
+ # Website location on server.
109
+ def siteroot
110
+ "/var/www/gforge-projects"
111
+ end
112
+
113
+ # What commands does this host support.
114
+
115
+ def commands
116
+ %w{ touch release publish post }
117
+ end
118
+
119
+
120
+ # Login to website.
121
+
122
+ def login # :yield:
123
+ load_project_cached
124
+
125
+ page = uri + "/account/login.php"
126
+ page.scheme = 'https'
127
+ page = URI.parse(page.to_s) # set SSL port correctly
128
+
129
+ form = {
130
+ "return_to" => "",
131
+ "form_loginname" => username,
132
+ "form_pw" => password,
133
+ "login" => "Login with SSL"
134
+ }
135
+ html = http_post(page, form)
136
+
137
+ if not html[/Personal Page/]
138
+ puts "Login failed."
139
+ re1 = Regexp.escape(%{<h2 style="color:red">})
140
+ re2 = Regexp.escape(%{</h2>})
141
+ html[/#{re1}(.*?)#{re2}/]
142
+ raise $1
143
+ else
144
+ @printed_project_name ||= (puts "Project: #{unixname}"; true)
145
+ end
146
+
147
+ if block_given?
148
+ begin
149
+ yield
150
+ ensure
151
+ logout
152
+ end
153
+ end
154
+ end
155
+
156
+ # Logout of website.
157
+
158
+ def logout
159
+ page = "/account/logout.php"
160
+ form = {}
161
+ http_post(page, form)
162
+ end
163
+
164
+ # Touch base with server -- login and logout.
165
+
166
+ def touch(options={})
167
+ login
168
+ puts "Group ID: #{group_id}"
169
+ puts "Login/Logout successful."
170
+ logout
171
+ end
172
+
173
+ # Upload release packages to hosting service.
174
+ #
175
+ # This task releases files to RubyForge --it should work with other
176
+ # GForge instaces or SourceForge clones too.
177
+ #
178
+ # While defaults are nice, you may want a little more control. You can
179
+ # specify additional attributes:
180
+ #
181
+ # files package files to release.
182
+ # exclude Package formats to exclude from files.
183
+ # (from those created by pack)
184
+ # unixname Project name on host.
185
+ # package Package to which this release belongs (defaults to project)
186
+ # release Release name (default is version number)
187
+ # version Version of release
188
+ # date Date of release (defaults to Time.now)
189
+ # processor Processor/Architecture (any, i386, PPC, etc.)
190
+ # is_public Public release? (defualts to true)
191
+ #
192
+ # Rubyforge release also take two documentation elements.
193
+ # These can be either the straight text, or a file.
194
+ #
195
+ # changes Change log text
196
+ # notes Release notes text
197
+ #
198
+ # or
199
+ #
200
+ # changelog Change log file
201
+ # notelog Release notes file
202
+ #
203
+ # The release option can be a template by using %s in the
204
+ # string. The version number of your project will be sub'd
205
+ # in for the %s. This saves you from having to update
206
+ # the release name before every release.
207
+ #
208
+ #--
209
+ # What about releasing a pacman PKGBUILD?
210
+ #++
211
+
212
+ def release(options)
213
+ options = options.rekey
214
+
215
+ version = options[:version] || version()
216
+
217
+ changes = options[:changes]
218
+ notes = options[:notes]
219
+
220
+ changelog = options[:changelog]
221
+ notelog = options[:notelog]
222
+
223
+ unixname = options[:unixname] || unixname()
224
+ package = options[:package] || unixname()
225
+ release = options[:release] || version()
226
+ name = options[:name] || package
227
+ files = options[:files] || options[:file] || []
228
+ date = options[:date] || Time::now.strftime('%Y-%m-%d %H:%M')
229
+ processor = options[:processor] || 'Any'
230
+ store = options[:store] || 'pkg'
231
+
232
+ is_public = options[:is_public].nil? ? true : options[:is_public]
233
+
234
+ raise ArgumentError, "missing unixname" unless unixname
235
+ raise ArgumentError, "missing package" unless package
236
+ raise ArgumentError, "missing release" unless release
237
+
238
+ if notelog
239
+ release_notes = ::IO::read(notelog) if release_notes and test(?f, release_notes)
240
+ else
241
+ release_notes = notes
242
+ end
243
+
244
+ if changelog
245
+ release_changes = ::IO::read(changelog) if release_changes and test(?f, release_changes)
246
+ else
247
+ release_changes = changes
248
+ end
249
+
250
+ # Gather package files to release.
251
+ if files.empty?
252
+ files = Dir[File.join(store, '*')].select do |file|
253
+ /#{version}[.]/ =~ file
254
+ end
255
+ #files = Dir.glob(File.join(store,"#{name}-#{version}*"))
256
+ end
257
+ files = files.select{ |f| File.file?(f) }
258
+
259
+ abort "No package files." if files.empty?
260
+
261
+ files.each do |file|
262
+ abort "Not a file -- #{file}" unless File.exist?(file)
263
+ puts "Release file: #{file}"
264
+ end
265
+
266
+ # which package types
267
+ #rtypes = [ 'tgz', 'tbz', 'tar.gz', 'tar.bz2', 'deb', 'gem', 'ebuild', 'zip' ]
268
+ #rtypes -= exclude
269
+ #rtypes = rtypes.collect{ |rt| Regexp.escape( rt ) }
270
+ #re_rtypes = Regexp.new('[.](' << rtypes.join('|') << ')$')
271
+
272
+ puts "Releasing #{package} #{release}..." #unless options['quiet']
273
+
274
+ login do
275
+
276
+ raise ArgumentError, "missing group_id" unless group_id
277
+
278
+ unless package_id = package?(package)
279
+ if dryrun?
280
+ puts "Package '#{package}' does not exist."
281
+ puts "Create package #{package}."
282
+ abort "Cannot continue in dryrun mode."
283
+ else
284
+ #unless options['force']
285
+ q = "Package '#{package}' does not exist. Create?"
286
+ a = ask(q, 'yN')
287
+ abort "Task canceled." unless ['y', 'yes', 'okay'].include?(a.downcase)
288
+ #end
289
+ puts "Creating package #{package}..."
290
+ create_package(package, is_public)
291
+ unless package_id = package?(package)
292
+ raise "Package creation failed."
293
+ end
294
+ end
295
+ end
296
+ if release_id = release?(release, package_id)
297
+ #unless options[:force]
298
+ if dryrun?
299
+ puts "Release #{release} already exists."
300
+ else
301
+ q = "Release #{release} already exists. Re-release?"
302
+ a = ask(q, 'yN')
303
+ abort "Task canceled." unless ['y', 'yes', 'okay'].include?(a.downcase)
304
+ #puts "Use -f option to force re-release."
305
+ #return
306
+ end
307
+ files.each do |file|
308
+ fname = File.basename(file)
309
+ if file_id = file?(fname, package)
310
+ if dryrun?
311
+ puts "Remove file #{fname}."
312
+ else
313
+ puts "Removing file #{fname}..."
314
+ remove_file(file_id, release_id, package_id)
315
+ end
316
+ end
317
+ if dryrun?
318
+ puts "Add file #{fname}."
319
+ else
320
+ puts "Adding file #{fname}..."
321
+ add_file(file, release_id, package_id, processor)
322
+ end
323
+ end
324
+ else
325
+ if dryrun?
326
+ puts "Add release #{release}."
327
+ else
328
+ puts "Adding release #{release}..."
329
+ add_release(release, package_id, files,
330
+ :processor => processor,
331
+ :release_date => date,
332
+ :release_changes => release_changes,
333
+ :release_notes => release_notes,
334
+ :preformatted => '1'
335
+ )
336
+ unless release_id = release?(release, package_id)
337
+ raise "Release creation failed."
338
+ end
339
+ end
340
+ #files.each do |file|
341
+ # puts "Added file #{File.basename(file)}."
342
+ #end
343
+ end
344
+ end
345
+ puts "Release complete!" unless dryrun?
346
+ end
347
+
348
+ # Publish documents to website.
349
+ #
350
+ # TODO: Allow sitemap to hande single files.
351
+ # This requires adjusting filter system though.
352
+ #
353
+ def publish(options)
354
+ options = options.rekey(&:to_s)
355
+
356
+ raise "no username" unless username
357
+
358
+ sitemap = options['sitemap']
359
+ filter = options['filter']
360
+ quiet = options['quiet'] || !verbose? #quiet?
361
+ verbose = options['verbose'] || verbose?
362
+ dryrun = %w{dryrun noharm pretend}.any?{ |x| options[x] } || dryrun?
363
+
364
+ case sitemap
365
+ when Hash
366
+ when Array
367
+ sitemap.inject({}) do |h, (s, d)|
368
+ h[s] = d; h
369
+ end
370
+ else
371
+ sitemap = { sitemap.to_s => '.' }
372
+ end
373
+
374
+ sitemap.each do |from, to|
375
+ if !File.directory?(from)
376
+ raise ArgumentError, "Non-existant publishing directory -- #{from}."
377
+ end
378
+ end
379
+
380
+ #source = options['source']
381
+ #subdir = options['output'] || options['subdir']
382
+
383
+ sitemap.each do |source, subdir|
384
+ if subdir and subdir != '.'
385
+ destination = File.join(unixname, subdir)
386
+ else
387
+ destination = unixname
388
+ end
389
+
390
+ dir = source.to_s.chomp('/') + '/'
391
+ url = "#{username}@rubyforge.org:/var/www/gforge-projects/#{destination}"
392
+
393
+ op = ['-rLvz', '--delete-after'] # maybe -p ?
394
+
395
+ op << "-n" if dryrun
396
+ op << "-q" if quiet
397
+ op << "-v" if verbose
398
+ op << "--progress" unless quiet
399
+
400
+ # custom filter
401
+ op << "--filter='. #{filter}'" if filter
402
+
403
+ create_rsync_filter(source)
404
+
405
+ # per dir-merge filter
406
+ op << "--filter=': .rsync-filter'"
407
+
408
+ args = op + [dir, url]
409
+
410
+ if dryrun? # Actually rsync supports a dryrun mode. We could let this go thrugh ?
411
+ puts "rsync #{args.to_params}"
412
+ else
413
+ system "rsync #{args.to_params}"
414
+ # UploadUtils.rsync(options)
415
+ #system "#{op.join(' ')} #{dir} #{url}"
416
+ end
417
+ end
418
+ end
419
+
420
+ private
421
+
422
+ #
423
+ def create_rsync_filter(source)
424
+ protect = %w{usage statcvs statsvn robot.txt robots.txt wiki}
425
+ exclude = %w{.svn .gitignore}
426
+
427
+ rsync_file = File.join(source,'.rsync-filter')
428
+ unless FileTest.file?(rsync_file)
429
+ File.open(rsync_file, 'w') do |f|
430
+ exclude.each{|e| f << "- #{e}\n"}
431
+ protect.each{|e| f << "P #{e}\n"}
432
+ end
433
+ end
434
+
435
+ #protect.to_list.each do |x|
436
+ # s = x[0,1]
437
+ # x = x[1..-1] if s == '-' or s == '+'
438
+ # s == '-' ? protect.delete(x) : protect << x
439
+ #end
440
+
441
+ #exclude.to_list.each do |x|
442
+ # s = x[0,1]
443
+ # x = x[1..-1] if s == '-' or s == '+'
444
+ # s == '-' ? exclude.delete(x) : exclude << x
445
+ #end
446
+ end
447
+
448
+ public
449
+
450
+ # Submit a news item.
451
+
452
+ def announce(options)
453
+ options = options.rekey
454
+
455
+ if file = options[:file]
456
+ text = File.read(file).strip
457
+ i = text.index("\n")
458
+ subject = text[0...i].strip
459
+ message = text[i..-1].strip
460
+ else
461
+ subject = options[:subject] || "Announcing #{unixname}!"
462
+ message = options[:message] || options[:body]
463
+ end
464
+
465
+ if dryrun?
466
+ puts "announce-rubyforge: #{subject}"
467
+ else
468
+ post_news(subject, message)
469
+ puts "News item posted!"
470
+ end
471
+ end
472
+
473
+
474
+ private
475
+
476
+ # HTTP POST transaction.
477
+
478
+ def http_post(page, form, extheader={})
479
+ client = HTTPClient::new ENV["HTTP_PROXY"]
480
+ client.debug_dev = STDERR if ENV["REAP_DEBUG"] || $DEBUG
481
+ client.set_cookie_store(cookie_jar)
482
+ client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
483
+
484
+ # HACK to fix http-client redirect bug/feature
485
+ client.redirect_uri_callback = lambda do |uri, res|
486
+ page = res.header['location'].first
487
+ page =~ %r/http/ ? page : @uri + page
488
+ end
489
+
490
+ uri = @uri + page
491
+ if $DEBUG then
492
+ puts "POST #{uri.inspect}"
493
+ puts "#{form.inspect}"
494
+ puts "#{extheader.inspect}" unless extheader.empty?
495
+ puts
496
+ end
497
+
498
+ response = client.post_content uri, form, extheader
499
+
500
+ if response[REPORT]
501
+ puts "(" + $1 + ")"
502
+ end
503
+
504
+ client.save_cookie_store
505
+
506
+ return response
507
+ end
508
+
509
+ #
510
+
511
+ def load_project_cached
512
+ @load_project_cache ||= load_project
513
+ end
514
+
515
+ # Loads information for project: group_id, package_ids and release_ids.
516
+
517
+ def load_project
518
+ html = URI.parse("http://#{domain}/projects/#{unixname}/index.html").read
519
+
520
+ group_id = html[/(frs|tracker)\/\?group_id=\d+/][/\d+/].to_i
521
+ @group_id = group_id
522
+
523
+ if $DEBUG
524
+ puts "GROUP_ID = #{group_id}"
525
+ end
526
+
527
+ html = URI.parse("http://rubyforge.org/frs/?group_id=#{group_id}").read
528
+
529
+ package = nil
530
+ html.scan(/<h3>[^<]+|release_id=\d+">[^>]+|filemodule_id=\d+/).each do |s|
531
+ case s
532
+ when /<h3>([^<]+)/ then
533
+ package = $1.strip
534
+ when /filemodule_id=(\d+)/ then
535
+ @package_ids[package] = $1.to_i
536
+ when /release_id=(\d+)">([^<]+)/ then
537
+ package_id = @package_ids[package]
538
+ @release_ids[[package_id,$2]] = $1.to_i
539
+ end
540
+ end
541
+
542
+ if $DEBUG
543
+ p @package_ids, @release_ids
544
+ end
545
+ end
546
+
547
+ # Returns password. If not already set, will ask for it.
548
+
549
+ def password
550
+ @password ||= ENV['RUBYFORGE_PASSWORD']
551
+ @password ||= (
552
+ print "Password for #{username}: "
553
+ until inp = $stdin.gets ; sleep 1 ; end ; puts
554
+ inp.strip
555
+ )
556
+ end
557
+
558
+ # Package exists? Returns package-id number.
559
+
560
+ def package?(package_name)
561
+ id = @package_ids[package_name]
562
+ return id if id
563
+
564
+ package_id = nil
565
+
566
+ page = "/frs/"
567
+
568
+ form = {
569
+ "group_id" => group_id
570
+ }
571
+ scrape = http_post(page, form)
572
+
573
+ restr = ''
574
+ restr << Regexp.escape( package_name )
575
+ restr << '\s*'
576
+ restr << Regexp.escape( '<a href="/frs/monitor.php?filemodule_id=' )
577
+ restr << '(\d+)'
578
+ restr << Regexp.escape( %{&group_id=#{group_id}} )
579
+ re = Regexp.new( restr )
580
+
581
+ md = re.match( scrape )
582
+ if md
583
+ package_id = md[1]
584
+ end
585
+
586
+ @package_ids[package_name] = package_id
587
+ end
588
+
589
+ # Create a new package.
590
+
591
+ def create_package( package_name, is_public=true )
592
+ page = "/frs/admin/index.php"
593
+
594
+ form = {
595
+ "func" => "add_package",
596
+ "group_id" => group_id,
597
+ "package_name" => package_name,
598
+ "is_public" => (is_public ? 1 : 0),
599
+ "submit" => "Create This Package"
600
+ }
601
+
602
+ http_post(page, form)
603
+ end
604
+
605
+ # Delete package.
606
+
607
+ def delete_package(package_id)
608
+ page = "/frs/admin/index.php"
609
+
610
+ form = {
611
+ "func" => "delete_package",
612
+ "group_id" => group_id,
613
+ "package_id" => package_id,
614
+ "sure" => "1",
615
+ "really_sure" => "1",
616
+ "submit" => "Delete",
617
+ }
618
+
619
+ http_post(page, form)
620
+ end
621
+
622
+ # Release exits? Returns release-id number.
623
+
624
+ def release?(release_name, package_id)
625
+ id = @release_ids[[release_name,package_id]]
626
+ return id if id
627
+
628
+ release_id = nil
629
+
630
+ page = "/frs/admin/showreleases.php"
631
+
632
+ form = {
633
+ "package_id" => package_id,
634
+ "group_id" => group_id
635
+ }
636
+ scrape = http_post( page, form )
637
+
638
+ restr = ''
639
+ restr << Regexp.escape( %{"editrelease.php?group_id=#{group_id}} )
640
+ restr << Regexp.escape( %{&amp;package_id=#{package_id}} )
641
+ restr << Regexp.escape( %{&amp;release_id=} )
642
+ restr << '(\d+)'
643
+ restr << Regexp.escape( %{">#{release_name}} )
644
+ re = Regexp.new( restr )
645
+
646
+ md = re.match( scrape )
647
+ if md
648
+ release_id = md[1]
649
+ end
650
+
651
+ @release_ids[[release_name,package_id]] = release_id
652
+ end
653
+
654
+ # Add a new release.
655
+
656
+ def add_release(release_name, package_id, *files)
657
+ page = "/frs/admin/qrs.php"
658
+
659
+ options = (Hash===files.last ? files.pop : {}).rekey
660
+ files = files.flatten
661
+
662
+ processor = options[:processor]
663
+ release_date = options[:release_date]
664
+ release_changes = options[:release_changes]
665
+ release_notes = options[:release_notes]
666
+
667
+ release_date ||= Time::now.strftime("%Y-%m-%d %H:%M")
668
+
669
+ file = files.shift
670
+ puts "Adding file #{File.basename(file)}..."
671
+ userfile = open(file, 'rb')
672
+
673
+ type_id = userfile.path[%r|\.[^\./]+$|]
674
+ type_id = FILETYPES[type_id]
675
+ processor_id = PROCESSORS[processor.downcase]
676
+
677
+ preformatted = '1'
678
+
679
+ form = {
680
+ "group_id" => group_id,
681
+ "package_id" => package_id,
682
+ "release_name" => release_name,
683
+ "release_date" => release_date,
684
+ "type_id" => type_id,
685
+ "processor_id" => processor_id,
686
+ "release_notes" => release_notes,
687
+ "release_changes" => release_changes,
688
+ "preformatted" => preformatted,
689
+ "userfile" => userfile,
690
+ "submit" => "Release File"
691
+ }
692
+
693
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
694
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
695
+
696
+ html = http_post(page, form, 'content-type' => boundary)
697
+
698
+ release_id = html[/release_id=\d+/][/\d+/].to_i
699
+ puts "RELEASE ID = #{release_id}" if $DEBUG
700
+
701
+ files.each do |file|
702
+ puts "Adding file #{File.basename(file)}..."
703
+ add_file(file, release_id, package_id, processor)
704
+ end
705
+
706
+ release_id
707
+ end
708
+
709
+ # File exists?
710
+ #
711
+ # NOTE this is a bit fragile. If two releases have the same exact
712
+ # file name in them there could be a problem --that's probably not
713
+ # likely, but I can't yet rule it out.
714
+ #
715
+ # TODO Remove package argument, it is no longer needed.
716
+
717
+ def file?(file, package)
718
+ id = @file_ids[[file, package]]
719
+ return id if id
720
+
721
+ file_id = nil
722
+
723
+ page = "/frs/"
724
+
725
+ form = {
726
+ "group_id" => group_id
727
+ }
728
+ scrape = http_post(page, form)
729
+
730
+ restr = ''
731
+ #restr << Regexp.escape( package )
732
+ #restr << '\s*'
733
+ restr << Regexp.escape( '<a href="/frs/download.php/' )
734
+ restr << '(\d+)'
735
+ restr << Regexp.escape( %{/#{file}} )
736
+ re = Regexp.new(restr)
737
+
738
+ md = re.match(scrape)
739
+ if md
740
+ file_id = md[1]
741
+ end
742
+
743
+ @file_ids[[file, package]] = file_id
744
+ end
745
+
746
+ # Remove file from release.
747
+
748
+ def remove_file(file_id, release_id, package_id)
749
+ page="/frs/admin/editrelease.php"
750
+
751
+ form = {
752
+ "group_id" => group_id,
753
+ "package_id" => package_id,
754
+ "release_id" => release_id,
755
+ "file_id" => file_id,
756
+ "step3" => "Delete File",
757
+ "im_sure" => '1',
758
+ "submit" => "Delete File "
759
+ }
760
+
761
+ http_post(page, form)
762
+ end
763
+
764
+ #
765
+ # Add file to release.
766
+ #
767
+
768
+ def add_file(file, release_id, package_id, processor=nil)
769
+ page = '/frs/admin/editrelease.php'
770
+
771
+ userfile = open file, 'rb'
772
+
773
+ type_id = userfile.path[%r|\.[^\./]+$|]
774
+ type_id = FILETYPES[type_id]
775
+ processor_id = PROCESSORS[processor.downcase]
776
+
777
+ form = {
778
+ "step2" => '1',
779
+ "group_id" => group_id,
780
+ "package_id" => package_id,
781
+ "release_id" => release_id,
782
+ "userfile" => userfile,
783
+ "type_id" => type_id,
784
+ "processor_id" => processor_id,
785
+ "submit" => "Add This File"
786
+ }
787
+
788
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
789
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
790
+
791
+ http_post(page, form, 'content-type' => boundary)
792
+ end
793
+
794
+ # Posts news item to +group_id+ (can be name) with +subject+ and +body+
795
+
796
+ def post_news(subject, body)
797
+ page = "/news/submit.php"
798
+
799
+ subject % [unixname, version]
800
+
801
+ form = {
802
+ "group_id" => group_id,
803
+ "post_changes" => "y",
804
+ "summary" => subject,
805
+ "details" => body,
806
+ "submit" => "Submit"
807
+ }
808
+
809
+ login do
810
+ http_post(page, form)
811
+ end
812
+ end
813
+
814
+ # Constant for file types accepted by Rubyforge
815
+
816
+ FILETYPES = {
817
+ ".deb" => 1000,
818
+ ".rpm" => 2000,
819
+ ".zip" => 3000,
820
+ ".bz2" => 3100,
821
+ ".gz" => 3110,
822
+ ".src.zip" => 5000,
823
+ ".src.bz2" => 5010,
824
+ ".src.tar.bz2" => 5010,
825
+ ".src.gz" => 5020,
826
+ ".src.tar.gz" => 5020,
827
+ ".src.rpm" => 5100,
828
+ ".src" => 5900,
829
+ ".jpg" => 8000,
830
+ ".txt" => 8100,
831
+ ".text" => 8100,
832
+ ".htm" => 8200,
833
+ ".html" => 8200,
834
+ ".pdf" => 8300,
835
+ ".oth" => 9999,
836
+ ".ebuild" => 1300,
837
+ ".exe" => 1100,
838
+ ".dmg" => 1200,
839
+ ".tar.gz" => 3110,
840
+ ".tgz" => 3110,
841
+ ".gem" => 1400,
842
+ ".pgp" => 8150,
843
+ ".sig" => 8150
844
+ }
845
+
846
+ # Constant for processor types accepted by Rubyforge
847
+
848
+ PROCESSORS = {
849
+ "i386" => 1000,
850
+ "IA64" => 6000,
851
+ "Alpha" => 7000,
852
+ "Any" => 8000,
853
+ "PPC" => 2000,
854
+ "MIPS" => 3000,
855
+ "Sparc" => 4000,
856
+ "UltraSparc" => 5000,
857
+ "Other" => 9999,
858
+
859
+ "i386" => 1000,
860
+ "ia64" => 6000,
861
+ "alpha" => 7000,
862
+ "any" => 8000,
863
+ "ppc" => 2000,
864
+ "mips" => 3000,
865
+ "sparc" => 4000,
866
+ "ultrasparc" => 5000,
867
+ "other" => 9999,
868
+
869
+ "all" => 8000,
870
+ nil => 8000
871
+ }
872
+
873
+ end
874
+
875
+ end
876
+