rubyforge 1.0.4 → 2.0.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/History.txt +11 -0
- data/Manifest.txt +0 -2
- data/README.txt +1 -4
- data/Rakefile +6 -0
- data/bin/rubyforge +18 -34
- data/lib/rubyforge/client.rb +13 -34
- data/lib/rubyforge.rb +74 -181
- data/test/test_rubyforge.rb +128 -199
- data/test/test_rubyforge_client.rb +2 -67
- metadata +4 -9
- data/lib/rubyforge/cookie_manager.rb +0 -60
- data/test/test_rubyforge_cookie_manager.rb +0 -97
data/lib/rubyforge.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#! /usr/bin/env ruby -w
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
3
4
|
require 'enumerator'
|
|
4
5
|
require 'fileutils'
|
|
5
6
|
require 'yaml'
|
|
@@ -8,14 +9,15 @@ require 'rubyforge/client'
|
|
|
8
9
|
|
|
9
10
|
$TESTING = false unless defined? $TESTING
|
|
10
11
|
|
|
12
|
+
$DEBUG=false
|
|
13
|
+
|
|
11
14
|
class RubyForge
|
|
12
15
|
|
|
13
16
|
# :stopdoc:
|
|
14
|
-
VERSION = '
|
|
17
|
+
VERSION = '2.0.0'
|
|
15
18
|
HOME = ENV["HOME"] || ENV["HOMEPATH"] || File::expand_path("~")
|
|
16
19
|
RUBYFORGE_D = File::join HOME, ".rubyforge"
|
|
17
20
|
CONFIG_F = File::join RUBYFORGE_D, "user-config.yml"
|
|
18
|
-
COOKIE_F = File::join RUBYFORGE_D, "cookie.dat"
|
|
19
21
|
|
|
20
22
|
# We must use __FILE__ instead of DATA because this is now a library
|
|
21
23
|
# and DATA is relative to $0, not __FILE__.
|
|
@@ -36,6 +38,10 @@ class RubyForge
|
|
|
36
38
|
@client = nil
|
|
37
39
|
@uri = nil
|
|
38
40
|
end
|
|
41
|
+
|
|
42
|
+
# These are no-ops now, but we'll keep them here for backwards compatibility
|
|
43
|
+
def login ; end
|
|
44
|
+
def logout ; end
|
|
39
45
|
|
|
40
46
|
def configure opts = {}
|
|
41
47
|
user_path = CONFIG_F
|
|
@@ -56,7 +62,6 @@ class RubyForge
|
|
|
56
62
|
|
|
57
63
|
raise "no <username>" unless @userconfig["username"]
|
|
58
64
|
raise "no <password>" unless @userconfig["password"]
|
|
59
|
-
raise "no <cookie_jar>" unless @userconfig["cookie_jar"]
|
|
60
65
|
|
|
61
66
|
self
|
|
62
67
|
end
|
|
@@ -65,10 +70,6 @@ class RubyForge
|
|
|
65
70
|
@userconfig['force']
|
|
66
71
|
end
|
|
67
72
|
|
|
68
|
-
def cookie_store
|
|
69
|
-
client.cookie_store
|
|
70
|
-
end
|
|
71
|
-
|
|
72
73
|
def uri
|
|
73
74
|
@uri ||= URI.parse @userconfig['uri']
|
|
74
75
|
end
|
|
@@ -82,7 +83,6 @@ class RubyForge
|
|
|
82
83
|
open(CONFIG_F, "w") { |f|
|
|
83
84
|
f.write YAML.dump(config)
|
|
84
85
|
}
|
|
85
|
-
FileUtils::touch COOKIE_F
|
|
86
86
|
edit = (ENV["EDITOR"] || ENV["EDIT"] || "vi") + " '#{CONFIG_F}'"
|
|
87
87
|
system edit or puts "edit '#{CONFIG_F}'"
|
|
88
88
|
end
|
|
@@ -99,18 +99,21 @@ class RubyForge
|
|
|
99
99
|
%w(group package processor release).each do |type|
|
|
100
100
|
@autoconfig["#{type}_ids"].clear if @autoconfig["#{type}_ids"]
|
|
101
101
|
end
|
|
102
|
+
|
|
103
|
+
json = get_via_rest_api "/users/#{username}/groups.js"
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
html = URI.parse("http://rubyforge.org/users/#{username}/index.html").read
|
|
105
|
-
|
|
106
|
-
projects = html.scan(%r%/projects/([^/]+)/%).flatten
|
|
107
|
-
|
|
105
|
+
projects = json.collect {|group| group['group']['unix_group_name'] }
|
|
108
106
|
puts "Fetching #{projects.size} projects"
|
|
109
107
|
projects.each do |project|
|
|
110
|
-
next if project == "support"
|
|
111
108
|
scrape_project(project)
|
|
112
109
|
end
|
|
113
110
|
end
|
|
111
|
+
|
|
112
|
+
def get_via_rest_api(path)
|
|
113
|
+
url = "#{@userconfig['uri']}#{path}"
|
|
114
|
+
puts "Hitting REST API: #{url}" if $DEBUG
|
|
115
|
+
JSON.parse(client.get_content(url, {}, {}, @userconfig))
|
|
116
|
+
end
|
|
114
117
|
|
|
115
118
|
def scrape_project(project)
|
|
116
119
|
data = {
|
|
@@ -120,45 +123,30 @@ class RubyForge
|
|
|
120
123
|
"release_ids" => Hash.new { |h,k| h[k] = {} },
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
puts "Updating #{project}"
|
|
124
|
-
|
|
125
126
|
unless data["group_ids"].has_key? project then
|
|
126
|
-
|
|
127
|
-
group_id =
|
|
128
|
-
|
|
127
|
+
json = get_via_rest_api "/groups/#{project}.js"
|
|
128
|
+
group_id = json["group"]["group_id"].to_i
|
|
129
129
|
data["group_ids"][project] = group_id
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
package = $1.strip
|
|
141
|
-
when /release_id=(\d+)">([^<]+)/ then
|
|
142
|
-
data["release_ids"][package][$2] = $1.to_i
|
|
143
|
-
when /filemodule_id=(\d+)/ then
|
|
144
|
-
data["package_ids"][package] = $1.to_i
|
|
132
|
+
# Get project's packages
|
|
133
|
+
json = get_via_rest_api "/groups/#{project}/packages.js"
|
|
134
|
+
json.each do |package|
|
|
135
|
+
data["package_ids"][package["package"]["name"]] = package["package"]["package_id"]
|
|
136
|
+
# Get releases for this package
|
|
137
|
+
json = get_via_rest_api "/packages/#{package["package"]["package_id"]}/releases.js"
|
|
138
|
+
json.each do |release|
|
|
139
|
+
data["release_ids"][package["package"]["name"]][release["name"]] = release["release_id"]
|
|
145
140
|
end
|
|
146
141
|
end
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
html = client.get_content "http://rubyforge.org/frs/admin/qrs.php?package=&group_id=#{group_id}"
|
|
156
|
-
|
|
157
|
-
html =~ /<select name="processor_id">(.*?)<\/select>/m
|
|
158
|
-
processors = $1
|
|
159
|
-
processors.scan(/<option value="(\d{4})">([^<]+)/) do
|
|
160
|
-
data["processor_ids"][$2] = $1.to_i
|
|
161
|
-
end if processors
|
|
143
|
+
# Get processor ids
|
|
144
|
+
if @autoconfig['processor_ids'].nil? || @autoconfig['processor_ids'].empty?
|
|
145
|
+
puts "Fetching processor ids" if $DEBUG
|
|
146
|
+
json = get_via_rest_api "/processors.js"
|
|
147
|
+
json.each do |processor|
|
|
148
|
+
data["processor_ids"][processor["processor"]["name"]] = processor["processor"]["processor_id"]
|
|
149
|
+
end
|
|
162
150
|
end
|
|
163
151
|
|
|
164
152
|
data.each do |key, val|
|
|
@@ -169,51 +157,16 @@ class RubyForge
|
|
|
169
157
|
save_autoconfig
|
|
170
158
|
end
|
|
171
159
|
|
|
172
|
-
def logout
|
|
173
|
-
cookie_store.clear "rubyforge.org"
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def login
|
|
177
|
-
return if(!force and cookie_store['rubyforge.org']['session_ser']) rescue false
|
|
178
|
-
|
|
179
|
-
page = self.uri + "/account/login.php"
|
|
180
|
-
page.scheme = 'https'
|
|
181
|
-
page = URI.parse page.to_s # set SSL port correctly
|
|
182
|
-
|
|
183
|
-
username = @userconfig["username"]
|
|
184
|
-
password = @userconfig["password"]
|
|
185
|
-
|
|
186
|
-
form = {
|
|
187
|
-
"return_to" => "",
|
|
188
|
-
"form_loginname" => username,
|
|
189
|
-
"form_pw" => password,
|
|
190
|
-
"login" => "Login"
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
response = run page, form
|
|
194
|
-
|
|
195
|
-
re = %r/personal\s+page/iom
|
|
196
|
-
unless response =~ re
|
|
197
|
-
warn("%s:%d: warning: potentially failed login using %s" %
|
|
198
|
-
[__FILE__, __LINE__, username]) unless $TESTING
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
response
|
|
202
|
-
end
|
|
203
|
-
|
|
204
160
|
def create_package(group_id, package_name)
|
|
205
|
-
page = "/
|
|
161
|
+
page = "/groups/#{group_id}/packages"
|
|
206
162
|
|
|
207
163
|
group_id = lookup "group", group_id
|
|
208
164
|
is_private = @userconfig["is_private"]
|
|
209
165
|
is_public = is_private ? 0 : 1
|
|
210
166
|
|
|
211
167
|
form = {
|
|
212
|
-
"
|
|
213
|
-
"
|
|
214
|
-
"package_name" => package_name,
|
|
215
|
-
"is_public" => is_public,
|
|
216
|
-
"submit" => "Create This Package",
|
|
168
|
+
"package[name]" => package_name,
|
|
169
|
+
"package[is_public]" => is_public
|
|
217
170
|
}
|
|
218
171
|
|
|
219
172
|
run page, form
|
|
@@ -226,102 +179,61 @@ class RubyForge
|
|
|
226
179
|
# Posts news item to +group_id+ (can be name) with +subject+ and +body+
|
|
227
180
|
|
|
228
181
|
def post_news(group_id, subject, body)
|
|
229
|
-
|
|
230
|
-
group_id = lookup "group", group_id
|
|
231
|
-
|
|
182
|
+
# TODO - what was the post_changes parameter for?
|
|
232
183
|
form = {
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"summary" => subject,
|
|
236
|
-
"details" => body,
|
|
237
|
-
"submit" => "Submit",
|
|
184
|
+
"news_byte[summary]" => subject,
|
|
185
|
+
"news_byte[details]" => body
|
|
238
186
|
}
|
|
239
|
-
|
|
240
|
-
|
|
187
|
+
group_id = lookup "group", group_id
|
|
188
|
+
url = "/groups/#{group_id}/news_bytes"
|
|
189
|
+
run url, form
|
|
241
190
|
end
|
|
242
191
|
|
|
243
192
|
def delete_package(group_id, package_id)
|
|
244
|
-
page = "/frs/admin/index.php"
|
|
245
|
-
|
|
246
193
|
group_id = lookup "group", group_id
|
|
247
194
|
package_id = lookup "package", package_id
|
|
248
|
-
|
|
249
|
-
form = {
|
|
250
|
-
"func" => "delete_package",
|
|
251
|
-
"group_id" => group_id,
|
|
252
|
-
"package_id" => package_id,
|
|
253
|
-
"sure" => "1",
|
|
254
|
-
"really_sure" => "1",
|
|
255
|
-
"submit" => "Delete",
|
|
256
|
-
}
|
|
257
|
-
|
|
258
195
|
package_name = @autoconfig["package_ids"].invert[package_id]
|
|
259
196
|
@autoconfig["package_ids"].delete package_name
|
|
260
197
|
@autoconfig["release_ids"].delete package_name
|
|
261
198
|
save_autoconfig
|
|
262
|
-
|
|
263
|
-
run
|
|
199
|
+
url = "/packages/#{package_id}"
|
|
200
|
+
run url, {"_method" => "delete"}
|
|
264
201
|
end
|
|
265
202
|
|
|
266
203
|
def add_release(group_id, package_id, release_name, *files)
|
|
267
|
-
userfile = files.shift
|
|
268
|
-
page = "/frs/admin/qrs.php"
|
|
269
|
-
|
|
270
204
|
group_id = lookup "group", group_id
|
|
271
205
|
package_id = lookup "package", package_id
|
|
272
|
-
userfile = open userfile, 'rb'
|
|
273
206
|
release_date = @userconfig["release_date"]
|
|
274
|
-
type_id = @userconfig["type_id"]
|
|
275
|
-
processor_id = @userconfig["processor_id"]
|
|
276
207
|
release_notes = @userconfig["release_notes"]
|
|
277
208
|
release_changes = @userconfig["release_changes"]
|
|
278
209
|
preformatted = @userconfig["preformatted"]
|
|
279
|
-
|
|
280
210
|
release_date ||= Time.now.strftime("%Y-%m-%d %H:%M")
|
|
281
|
-
|
|
282
|
-
type_id ||= userfile.path[%r|\.[^\./]+$|]
|
|
283
|
-
type_id = (lookup "type", type_id rescue lookup "type", ".oth")
|
|
284
|
-
|
|
285
|
-
processor_id ||= "Any"
|
|
286
|
-
processor_id = lookup "processor", processor_id
|
|
287
|
-
|
|
288
211
|
release_notes = IO::read(release_notes) if
|
|
289
212
|
test(?e, release_notes) if release_notes
|
|
290
|
-
|
|
291
213
|
release_changes = IO::read(release_changes) if
|
|
292
214
|
test(?e, release_changes) if release_changes
|
|
293
|
-
|
|
294
215
|
preformatted = preformatted ? 1 : 0
|
|
295
216
|
|
|
296
217
|
form = {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
"processor_id" => processor_id,
|
|
303
|
-
"release_notes" => release_notes,
|
|
304
|
-
"release_changes" => release_changes,
|
|
305
|
-
"preformatted" => preformatted,
|
|
306
|
-
"userfile" => userfile,
|
|
307
|
-
"submit" => "Release File"
|
|
218
|
+
"release[name]" => release_name,
|
|
219
|
+
"release[release_date]" => release_date,
|
|
220
|
+
"release[notes]" => release_notes,
|
|
221
|
+
"release[changes]" => release_changes,
|
|
222
|
+
"release[preformatted]" => preformatted,
|
|
308
223
|
}
|
|
309
224
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
raise "Invalid package_id #{package_id}" if html[/Invalid package_id/]
|
|
315
|
-
raise "You have already released this version." if html[/That filename already exists in this project/]
|
|
316
|
-
|
|
317
|
-
release_id = html[/release_id=\d+/][/\d+/].to_i rescue nil
|
|
318
|
-
|
|
225
|
+
url = "/packages/#{package_id}/releases"
|
|
226
|
+
json = run url, form
|
|
227
|
+
|
|
228
|
+
release_id = JSON.parse(json)["release_id"].to_i rescue nil
|
|
319
229
|
unless release_id then
|
|
320
|
-
puts
|
|
321
|
-
raise "Couldn't get release_id, upload failed
|
|
230
|
+
puts json if $DEBUG
|
|
231
|
+
raise "Couldn't get release_id, upload failed?"
|
|
322
232
|
end
|
|
323
233
|
|
|
324
|
-
|
|
234
|
+
# FIXME
|
|
235
|
+
#raise "Invalid package_id #{package_id}" if html[/Invalid package_id/]
|
|
236
|
+
#raise "You have already released this version." if html[/That filename already exists in this project/]
|
|
325
237
|
|
|
326
238
|
files.each do |file|
|
|
327
239
|
add_file(group_id, package_id, release_id, file)
|
|
@@ -346,35 +258,29 @@ class RubyForge
|
|
|
346
258
|
# add_file(1024, 1242, "0.8.0", "traits-0.8.0.gem")
|
|
347
259
|
|
|
348
260
|
def add_file(group_name, package_name, release_name, userfile)
|
|
349
|
-
page = '/frs/admin/editrelease.php'
|
|
350
261
|
type_id = @userconfig["type_id"]
|
|
351
262
|
group_id = lookup "group", group_name
|
|
352
263
|
package_id = lookup "package", package_name
|
|
353
264
|
release_id = (Integer === release_name) ? release_name : lookup("release", package_name)[release_name]
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
page = "/frs/admin/editrelease.php?group_id=#{group_id}&release_id=#{release_id}&package_id=#{package_id}"
|
|
265
|
+
url = "/releases/#{release_id}/files.js"
|
|
357
266
|
|
|
358
267
|
userfile = open userfile, 'rb'
|
|
359
268
|
|
|
360
269
|
type_id ||= userfile.path[%r|\.[^\./]+$|]
|
|
361
270
|
type_id = (lookup "type", type_id rescue lookup "type", ".oth")
|
|
362
271
|
|
|
272
|
+
processor_id = @userconfig["processor_id"]
|
|
363
273
|
processor_id ||= "Any"
|
|
364
274
|
processor_id = lookup "processor", processor_id
|
|
365
275
|
|
|
366
276
|
form = {
|
|
367
|
-
"
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
|
|
375
|
-
boundary = "multipart/form-data; boundary=___#{ boundary }___"
|
|
277
|
+
"file[filename]" => File.basename(userfile.path),
|
|
278
|
+
"file[processor_id]" => processor_id,
|
|
279
|
+
"file[type_id]" => type_id,
|
|
280
|
+
"contents" => userfile.read
|
|
281
|
+
}
|
|
376
282
|
|
|
377
|
-
run
|
|
283
|
+
run url, form
|
|
378
284
|
end
|
|
379
285
|
|
|
380
286
|
def client
|
|
@@ -382,29 +288,16 @@ class RubyForge
|
|
|
382
288
|
|
|
383
289
|
@client = RubyForge::Client::new ENV["HTTP_PROXY"]
|
|
384
290
|
@client.debug_dev = STDERR if ENV["RUBYFORGE_DEBUG"] || ENV["DEBUG"] || $DEBUG
|
|
385
|
-
@client.cookie_store = @userconfig["cookie_jar"]
|
|
386
291
|
|
|
387
292
|
@client
|
|
388
293
|
end
|
|
389
294
|
|
|
390
295
|
def run(page, form, extheader={}) # :nodoc:
|
|
391
296
|
uri = self.uri + page
|
|
392
|
-
if $DEBUG
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
response = client.post_content uri, form, extheader
|
|
397
|
-
|
|
398
|
-
client.save_cookie_store
|
|
399
|
-
|
|
400
|
-
if $DEBUG then
|
|
401
|
-
response.sub!(/\A.*end tabGenerator -->/m, '')
|
|
402
|
-
response.gsub!(/\t/, ' ')
|
|
403
|
-
response.gsub!(/\n{3,}/, "\n\n")
|
|
404
|
-
puts response
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
return response
|
|
297
|
+
puts "client.post_content #{uri.inspect}, #{form.inspect}, #{extheader.inspect}" if $DEBUG
|
|
298
|
+
response = client.post_content uri, form, extheader, @userconfig
|
|
299
|
+
puts response if $DEBUG
|
|
300
|
+
response
|
|
408
301
|
end
|
|
409
302
|
|
|
410
303
|
def lookup(type, val) # :nodoc:
|
|
@@ -421,11 +314,11 @@ __END__
|
|
|
421
314
|
#
|
|
422
315
|
# base rubyforge uri - store in #{ CONFIG_F }
|
|
423
316
|
#
|
|
424
|
-
uri : http://rubyforge.org
|
|
317
|
+
uri : http://api.rubyforge.org
|
|
425
318
|
#
|
|
426
319
|
# this must be your username
|
|
427
320
|
#
|
|
428
|
-
username :
|
|
321
|
+
username : tom
|
|
429
322
|
#
|
|
430
323
|
# this must be your password
|
|
431
324
|
#
|
|
@@ -433,7 +326,6 @@ __END__
|
|
|
433
326
|
#
|
|
434
327
|
# defaults for some values
|
|
435
328
|
#
|
|
436
|
-
cookie_jar : #{ COOKIE_F }
|
|
437
329
|
is_private : false
|
|
438
330
|
# AUTOCONFIG:
|
|
439
331
|
rubyforge :
|
|
@@ -442,6 +334,7 @@ __END__
|
|
|
442
334
|
#
|
|
443
335
|
group_ids :
|
|
444
336
|
codeforpeople : 1024
|
|
337
|
+
support : 5
|
|
445
338
|
#
|
|
446
339
|
# map your package names to their rubyforge ids
|
|
447
340
|
#
|