rubyforge 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/http-access2.rb CHANGED
@@ -24,7 +24,7 @@ require 'http-access2/cookie'
24
24
  module HTTPAccess2
25
25
  VERSION = '2.0.6'
26
26
  RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
27
- s = %w$Id: http-access2.rb 114 2005-09-13 03:20:38Z nahi $
27
+ s = %w$Id: http-access2.rb,v 1.1.1.1 2006/09/13 04:28:30 zenspider Exp $
28
28
  RCS_FILE, RCS_REVISION = s[1][/.*(?=,v$)/], s[2]
29
29
 
30
30
  SSLEnabled = begin
data/lib/rubyforge.rb ADDED
@@ -0,0 +1,337 @@
1
+ #! /usr/bin/env ruby -w
2
+
3
+ require 'enumerator'
4
+ require 'fileutils'
5
+ require 'http-access2'
6
+ require 'yaml'
7
+
8
+ $TESTING = false unless defined? $TESTING
9
+
10
+ # HACK to fix http-access2 cookie selection bug
11
+ module WebAgent::CookieUtils
12
+ alias :old_domain_match :domain_match
13
+ def domain_match(host, domain)
14
+ case domain
15
+ when /\d+\.\d+\.\d+\.\d+/
16
+ return (host == domain)
17
+ when '.'
18
+ return true
19
+ when /^\./
20
+ #return tail_match?(domain, host)
21
+ return tail_match?(host, domain)
22
+ else
23
+ return (host == domain)
24
+ end
25
+ end
26
+ end
27
+
28
+ class RubyForge
29
+ VERSION = "0.2.0"
30
+ HOME = ENV["HOME"] || ENV["HOMEPATH"] || File::expand_path("~")
31
+ RUBYFORGE_D = File::join HOME, ".rubyforge"
32
+ CONFIG_F = File::join RUBYFORGE_D, "config.yml"
33
+ COOKIE_F = File::join RUBYFORGE_D, "cookie.dat"
34
+
35
+ # We must use __FILE__ instead of DATA because this is now a library
36
+ # and DATA is relative to $0, not __FILE__.
37
+ CONFIG = File.read(__FILE__).split(/__END__/).last.gsub(/#\{(.*)\}/) { eval $1 }
38
+
39
+ attr_reader :config
40
+ attr_reader :client if $TESTING
41
+
42
+ def initialize(config=CONFIG_F, opts={})
43
+ @config = test(?e, config) ? IO::read(config) : CONFIG
44
+ @config = YAML.load(@config).merge(opts)
45
+
46
+ raise "no <username>" unless @config["username"]
47
+ raise "no <password>" unless @config["password"]
48
+ raise "no <cookie_jar>" unless @config["cookie_jar"]
49
+ end
50
+
51
+ def setup
52
+ FileUtils::mkdir_p RUBYFORGE_D unless test ?d, RUBYFORGE_D
53
+ test ?e, CONFIG_F and FileUtils::mv CONFIG_F, "#{ CONFIG_F }.bak"
54
+ open(CONFIG_F, "w") { |f| f.write CONFIG }
55
+ FileUtils::touch COOKIE_F
56
+ edit = (ENV["EDITOR"] || ENV["EDIT"] || "vi") + " #{ CONFIG_F }"
57
+ system edit or puts "edit #{ CONFIG_F }"
58
+ end
59
+
60
+ def login
61
+ page = "/account/login.php"
62
+
63
+ username = @config["username"]
64
+ password = @config["password"]
65
+
66
+ form = {
67
+ "return_to" => "",
68
+ "form_loginname" => username,
69
+ "form_pw" => password,
70
+ "login" => "Login"
71
+ }
72
+
73
+ run page, form
74
+ end
75
+
76
+ def scrape_config(username)
77
+ require 'open-uri'
78
+
79
+ data = {
80
+ "group_ids" => {},
81
+ "package_ids" => {},
82
+ "release_ids" => Hash.new { |h,k| h[k] = {} },
83
+ }
84
+
85
+ html = URI.parse("http://rubyforge.org/users/#{username}/index.html").read
86
+ html.scan(%r%/projects/([^/]+)/%).flatten.each do |project|
87
+ next if project == "support"
88
+ html = URI.parse("http://rubyforge.org/projects/#{project}/index.html").read
89
+
90
+ group_id = html[/(frs|tracker)\/\?group_id=\d+/][/\d+/].to_i
91
+ data["group_ids"][project] = group_id
92
+
93
+ html = URI.parse("http://rubyforge.org/frs/?group_id=#{group_id}").read
94
+
95
+ package = nil
96
+ html.scan(/<h3>\w+|release_id=\d+">[^>]+|filemodule_id=\d+/).each do |s|
97
+ case s
98
+ when /<h3>(\w+)/ then
99
+ package = $1
100
+ when /release_id=(\d+)">([^<]+)/ then
101
+ data["release_ids"][package][$2] = $1.to_i
102
+ when /filemodule_id=(\d+)/ then
103
+ data["package_ids"][package] = $1.to_i
104
+ end
105
+ end
106
+ end
107
+ data
108
+ end
109
+
110
+ def create_package(group_id, package_name)
111
+ page = "/frs/admin/index.php"
112
+
113
+ group_id = lookup "group", group_id
114
+ is_private = @config["is_private"]
115
+ is_public = is_private ? 0 : 1
116
+
117
+ form = {
118
+ "func" => "add_package",
119
+ "group_id" => group_id,
120
+ "package_name" => package_name,
121
+ "is_public" => is_public,
122
+ "submit" => "Create This Package",
123
+ }
124
+
125
+ run page, form
126
+ end
127
+
128
+ def delete_package(group_id, package_id)
129
+ page = "/frs/admin/index.php"
130
+
131
+ group_id = lookup "group", group_id
132
+ package_id = lookup "package", package_id
133
+
134
+ form = {
135
+ "func" => "delete_package",
136
+ "group_id" => group_id,
137
+ "package_id" => package_id,
138
+ "sure" => "1",
139
+ "really_sure" => "1",
140
+ "submit" => "Delete",
141
+ }
142
+
143
+ run page, form
144
+ end
145
+
146
+ def add_release(group_id, package_id, release_name, userfile)
147
+ # TODO: *userfiles
148
+ page = "/frs/admin/qrs.php"
149
+
150
+ group_id = lookup "group", group_id
151
+ package_id = lookup "package", package_id
152
+ userfile = open userfile
153
+ release_date = @config["release_date"]
154
+ type_id = @config["type_id"]
155
+ processor_id = @config["processor_id"]
156
+ release_notes = @config["release_notes"]
157
+ release_changes = @config["release_changes"]
158
+ preformatted = @config["preformatted"]
159
+
160
+ release_date ||= Time::now.strftime("%Y-%m-%d %H:%M")
161
+
162
+ type_id ||= userfile.path[%r|\.[^\./]+$|]
163
+ type_id = lookup "type", type_id
164
+
165
+ processor_id ||= "Any"
166
+ processor_id = lookup "processor", processor_id
167
+
168
+ release_notes = IO::read(release_notes) if release_notes and test(?e, release_notes)
169
+
170
+ release_changes = IO::read(release_changes) if release_changes and test(?e, release_changes)
171
+
172
+ preformatted = preformatted ? 1 : 0
173
+
174
+ form = {
175
+ "group_id" => group_id,
176
+ "package_id" => package_id,
177
+ "release_name" => release_name,
178
+ "release_date" => release_date,
179
+ "type_id" => type_id,
180
+ "processor_id" => processor_id,
181
+ "release_notes" => release_notes,
182
+ "release_changes" => release_changes,
183
+ "preformatted" => preformatted,
184
+ "userfile" => userfile,
185
+ "submit" => "Release File"
186
+ }
187
+
188
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
189
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
190
+
191
+ run page, form, 'content-type' => boundary
192
+ end
193
+
194
+ def add_file(group_name, package_name, release_name, userfile)
195
+ page = '/frs/admin/editrelease.php'
196
+ type_id = @config["type_id"]
197
+ group_id = lookup "group", group_name
198
+ package_id = lookup "package", package_name
199
+ release_id = (Integer === release_name) ? release_name : lookup("release", package_name)[release_name]
200
+ processor_id = @config["processor_id"]
201
+
202
+ page = "/frs/admin/editrelease.php?group_id=#{group_id}&release_id=#{release_id}&package_id=#{package_id}"
203
+
204
+ userfile = open userfile
205
+
206
+ type_id ||= userfile.path[%r|\.[^\./]+$|]
207
+ type_id = lookup "type", type_id
208
+
209
+ processor_id ||= "Any"
210
+ processor_id = lookup "processor", processor_id
211
+
212
+ form = {
213
+ "step2" => 1,
214
+ "type_id" => type_id,
215
+ "processor_id" => processor_id,
216
+ "userfile" => userfile,
217
+ "submit" => "Add This File"
218
+ }
219
+
220
+ boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
221
+ boundary = "multipart/form-data; boundary=___#{ boundary }___"
222
+
223
+ run page, form, 'content-type' => boundary
224
+ end
225
+
226
+ def run(page, form, extheader={})
227
+ client = HTTPAccess2::Client::new ENV["HTTP_PROXY"]
228
+ client.debug_dev = STDERR if ENV["RUBYFORGE_DEBUG"] || ENV["DEBUG"]
229
+ client.set_cookie_store @config["cookie_jar"]
230
+
231
+ # HACK to fix http-access2 redirect bug/feature
232
+ client.redirect_uri_callback = lambda do |res|
233
+ page = res.header['location'].first
234
+ page =~ %r/http/ ? page : "#{ @config['uri'] }/#{ page }"
235
+ end
236
+
237
+ response = client.post_content "#{ File.join(@config['uri'], page) }", form, extheader
238
+
239
+ @client = client if $TESTING
240
+
241
+ client.save_cookie_store
242
+
243
+ return response
244
+ end
245
+
246
+ def lookup(type, val)
247
+ unless Fixnum === val then
248
+ key = val.to_s
249
+ val = @config["rubyforge"]["#{type}_ids"][key]
250
+ raise "no <#{type}_id> configured for <#{ key }>" unless val
251
+ end
252
+ val
253
+ end
254
+ end
255
+
256
+ __END__
257
+ #
258
+ # base rubyforge uri - store in #{ CONFIG_F }
259
+ #
260
+ uri : http://rubyforge.org
261
+ #
262
+ # this must be your username
263
+ #
264
+ username : username
265
+ #
266
+ # this must be your password
267
+ #
268
+ password : password
269
+ #
270
+ # defaults for some values
271
+ #
272
+ cookie_jar : #{ COOKIE_F }
273
+ is_private : false
274
+ #
275
+ # server side rubyforge configuration
276
+ #
277
+ rubyforge :
278
+ #
279
+ # map your group names to their rubyforge ids
280
+ #
281
+ group_ids :
282
+ codeforpeople.com : 1024
283
+ #
284
+ # map your package names to their rubyforge ids
285
+ #
286
+ package_ids :
287
+ traits : 1241
288
+ #
289
+ # map your package names to their rubyforge ids
290
+ #
291
+ release_ids :
292
+ traits :
293
+ 1.2.3 : 666
294
+ #
295
+ # mapping file exts to rubyforge ids
296
+ #
297
+ type_ids :
298
+ .deb : 1000
299
+ .rpm : 2000
300
+ .zip : 3000
301
+ .bz2 : 3100
302
+ .gz : 3110
303
+ .src.zip : 5000
304
+ .src.bz2 : 5010
305
+ .src.tar.bz2 : 5010
306
+ .src.gz : 5020
307
+ .src.tar.gz : 5020
308
+ .src.rpm : 5100
309
+ .src : 5900
310
+ .jpg : 8000
311
+ .txt : 8100
312
+ .text : 8100
313
+ .htm : 8200
314
+ .html : 8200
315
+ .pdf : 8300
316
+ .oth : 9999
317
+ .ebuild : 1300
318
+ .exe : 1100
319
+ .dmg : 1200
320
+ .tar.gz : 5000
321
+ .tgz : 5000
322
+ .gem : 1400
323
+ .pgp : 8150
324
+ .sig : 8150
325
+ #
326
+ # map processor names to rubyforge ids
327
+ #
328
+ processor_ids :
329
+ i386 : 1000
330
+ IA64 : 6000
331
+ Alpha : 7000
332
+ Any : 8000
333
+ PPC : 2000
334
+ MIPS : 3000
335
+ Sparc : 4000
336
+ UltraSparc : 5000
337
+ Other : 9999
@@ -0,0 +1,229 @@
1
+ require 'test/unit' unless defined? $ZENTEST and $ZENTEST
2
+
3
+ $TESTING = true
4
+ require 'rubyforge'
5
+
6
+ class FakeRubyForge < RubyForge
7
+ attr_accessor :page, :form, :extheader
8
+ def run(page, form, extheader={})
9
+ @page, @form, @extheader = page, form, extheader
10
+ end
11
+ end
12
+
13
+ class HTTPAccess2::Client
14
+ attr_accessor :url, :form, :headers
15
+ alias :old_post_content :post_content
16
+ def post_content(url, form, headers)
17
+ @url, @form, @headers = url, form, headers
18
+ end
19
+ end
20
+
21
+ class TestRubyForge < Test::Unit::TestCase
22
+
23
+ def setup
24
+ srand(0)
25
+ util_new FakeRubyForge
26
+ end
27
+
28
+ def test_initialize_bad
29
+ assert_raise RuntimeError do
30
+ RubyForge.new(RubyForge::CONFIG_F, "username" => nil)
31
+ end
32
+ assert_raise RuntimeError do
33
+ RubyForge.new(RubyForge::CONFIG_F, "password" => nil)
34
+ end
35
+ assert_raise RuntimeError do
36
+ RubyForge.new(RubyForge::CONFIG_F, "cookie_jar" => nil)
37
+ end
38
+ end
39
+
40
+ def test_setup
41
+ # TODO raise NotImplementedError, 'Need to write test_setup'
42
+ end
43
+
44
+ def test_login
45
+ u, p = 'fooby', 's3kr3t'
46
+ @rubyforge.config['username'] = u
47
+ @rubyforge.config['password'] = p
48
+ @rubyforge.login
49
+
50
+ util_run('/account/login.php',
51
+ 'form_pw' => p,
52
+ 'form_loginname' => u,
53
+ 'return_to' => '',
54
+ 'login' => 'Login')
55
+ end
56
+
57
+ def test_create_package
58
+ @rubyforge.create_package(42, 'woot_pkg')
59
+
60
+ util_run('/frs/admin/index.php',
61
+ "submit" => "Create This Package",
62
+ "group_id" => 42,
63
+ "is_public" => 1,
64
+ "package_name" => "woot_pkg",
65
+ "func" => "add_package")
66
+ end
67
+
68
+ def test_delete_package
69
+ @rubyforge.delete_package(42, 666)
70
+ util_delete_package
71
+ end
72
+
73
+ def test_delete_package_package_name
74
+ @rubyforge.config["rubyforge"]["package_ids"]["woot_pkg"] = 666
75
+ @rubyforge.delete_package(42, "woot_pkg")
76
+ util_delete_package
77
+ end
78
+
79
+ def test_delete_package_undefined_package_name
80
+ assert_raise RuntimeError do
81
+ @rubyforge.delete_package(42, "blah")
82
+ end
83
+ end
84
+
85
+ def test_delete_package_group_name
86
+ @rubyforge.config["rubyforge"]["group_ids"]["seattlerb"] = 42
87
+ @rubyforge.delete_package("seattlerb", 666)
88
+ util_delete_package
89
+ end
90
+
91
+ def test_delete_package_undefined_group_name
92
+ assert_raise RuntimeError do
93
+ @rubyforge.delete_package("blah", 666)
94
+ end
95
+ end
96
+
97
+ def test_add_file
98
+ @rubyforge.config["rubyforge"]["group_ids"]["seattlerb"] = 666
99
+ @rubyforge.config["rubyforge"]["package_ids"]["ringy_dingy"] = 42
100
+ @rubyforge.config["rubyforge"]["release_ids"]["ringy_dingy"] ||= {}
101
+ @rubyforge.config["rubyforge"]["release_ids"]["ringy_dingy"]["1.2.3"] = 43
102
+
103
+ @rubyforge.add_file('seattlerb', 'ringy_dingy', '1.2.3', __FILE__)
104
+
105
+ util_run('/frs/admin/editrelease.php?group_id=666&release_id=43&package_id=42',
106
+ { "step2" => 1,
107
+ "type_id" => 9999,
108
+ "processor_id" => 8000,
109
+ "submit" => "Add This File"},
110
+ {"content-type"=> "multipart/form-data; boundary=___00__03__03__39__09__19__21__36___"})
111
+ end
112
+
113
+ def test_add_release
114
+ @rubyforge.add_release(42, 666, '1.2.3', __FILE__)
115
+ util_add_release
116
+ end
117
+
118
+ def test_add_release_package_name
119
+ @rubyforge.config["rubyforge"]["package_ids"]["woot_pkg"] = 666
120
+ @rubyforge.add_release(42, "woot_pkg", '1.2.3', __FILE__)
121
+ util_add_release
122
+ end
123
+
124
+ def test_add_release_undefined_package_name
125
+ assert_raise RuntimeError do
126
+ @rubyforge.add_release(42, "blah", '1.2.3', __FILE__)
127
+ end
128
+ end
129
+
130
+ def test_add_release_group_name
131
+ @rubyforge.config["rubyforge"]["group_ids"]["seattlerb"] = 42
132
+ @rubyforge.add_release("seattlerb", 666, '1.2.3', __FILE__)
133
+ util_add_release
134
+ end
135
+
136
+ def test_add_release_undefined_group_name
137
+ assert_raise RuntimeError do
138
+ @rubyforge.add_release("blah", 666, '1.2.3', __FILE__)
139
+ end
140
+ end
141
+
142
+ def test_lookup_id
143
+ assert_equal 43, @rubyforge.lookup("package", 43)
144
+ end
145
+
146
+ def test_lookup_string_number
147
+ assert_raise RuntimeError do
148
+ @rubyforge.lookup("package", "43")
149
+ end
150
+ end
151
+
152
+ def test_lookup_name
153
+ @rubyforge.config["rubyforge"]["package_ids"]["ringy_dingy"] = 42
154
+ assert_equal 42, @rubyforge.lookup("package", "ringy_dingy")
155
+ end
156
+
157
+ def test_lookup_undefined
158
+ assert_raise RuntimeError do
159
+ @rubyforge.lookup("package", "blah")
160
+ end
161
+ end
162
+
163
+ def test_run
164
+ util_new RubyForge
165
+ @rubyforge.add_release(42, 666, '1.2.3', __FILE__)
166
+
167
+ extheader = {"content-type"=> "multipart/form-data; boundary=___00__03__03__39__09__19__21__36___"}
168
+
169
+ form = {
170
+ "processor_id" => 8000,
171
+ "submit" => "Release File",
172
+ "preformatted" => 0,
173
+ "release_changes" => nil,
174
+ "type_id" => 9999,
175
+ "group_id" => 42,
176
+ "release_name" => "1.2.3",
177
+ "release_notes" => nil,
178
+ "package_id" => 666,
179
+ "release_date" => "today"
180
+ }
181
+
182
+ client = @rubyforge.client
183
+ assert client.form.delete("userfile")
184
+
185
+ assert_equal 'http://rubyforge.org/frs/admin/qrs.php', client.url
186
+ assert_equal form, client.form
187
+ assert_equal extheader, client.headers
188
+ end
189
+
190
+ def util_new(klass)
191
+ @rubyforge = klass.new
192
+ @rubyforge.config["release_date"] = "today"
193
+ @rubyforge.config["rubyforge"]["type_ids"][".rb"] = 9999
194
+ end
195
+
196
+ def util_run(page, form={}, extheader={})
197
+ form_result = @rubyforge.form
198
+ assert form_result.delete("userfile") unless extheader.empty?
199
+
200
+ assert_equal page, @rubyforge.page
201
+ assert_equal form, form_result
202
+ assert_equal extheader, @rubyforge.extheader
203
+ end
204
+
205
+ def util_add_release
206
+ util_run('/frs/admin/qrs.php',
207
+ {"processor_id" => 8000,
208
+ "submit" => "Release File",
209
+ "preformatted" => 0,
210
+ "release_changes" => nil,
211
+ "type_id" => 9999,
212
+ "group_id" => 42,
213
+ "release_name" => "1.2.3",
214
+ "release_notes" => nil,
215
+ "package_id" => 666,
216
+ "release_date" => "today"},
217
+ {"content-type"=> "multipart/form-data; boundary=___00__03__03__39__09__19__21__36___"})
218
+ end
219
+
220
+ def util_delete_package
221
+ util_run('/frs/admin/index.php',
222
+ "submit" => "Delete",
223
+ "really_sure" => "1",
224
+ "group_id" => 42,
225
+ "func" => "delete_package",
226
+ "package_id" => 666,
227
+ "sure" => "1")
228
+ end
229
+ end