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