rubyforge 0.1.1 → 0.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.
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