rhype 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+