codders-curb 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/LICENSE +51 -0
  2. data/README +194 -0
  3. data/Rakefile +320 -0
  4. data/doc.rb +42 -0
  5. data/ext/curb.c +977 -0
  6. data/ext/curb.h +52 -0
  7. data/ext/curb_easy.c +3404 -0
  8. data/ext/curb_easy.h +90 -0
  9. data/ext/curb_errors.c +647 -0
  10. data/ext/curb_errors.h +129 -0
  11. data/ext/curb_macros.h +159 -0
  12. data/ext/curb_multi.c +633 -0
  13. data/ext/curb_multi.h +26 -0
  14. data/ext/curb_postfield.c +523 -0
  15. data/ext/curb_postfield.h +40 -0
  16. data/ext/curb_upload.c +80 -0
  17. data/ext/curb_upload.h +30 -0
  18. data/ext/extconf.rb +399 -0
  19. data/lib/curb.rb +4 -0
  20. data/lib/curl.rb +57 -0
  21. data/lib/curl/easy.rb +468 -0
  22. data/lib/curl/multi.rb +248 -0
  23. data/tests/alltests.rb +3 -0
  24. data/tests/bug_crash_on_debug.rb +39 -0
  25. data/tests/bug_crash_on_progress.rb +33 -0
  26. data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
  27. data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +83 -0
  28. data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
  29. data/tests/bug_multi_segfault.rb +14 -0
  30. data/tests/bug_postfields_crash.rb +26 -0
  31. data/tests/bug_postfields_crash2.rb +57 -0
  32. data/tests/bug_require_last_or_segfault.rb +40 -0
  33. data/tests/bugtests.rb +9 -0
  34. data/tests/helper.rb +199 -0
  35. data/tests/mem_check.rb +65 -0
  36. data/tests/require_last_or_segfault_script.rb +36 -0
  37. data/tests/tc_curl_download.rb +75 -0
  38. data/tests/tc_curl_easy.rb +1011 -0
  39. data/tests/tc_curl_easy_setopt.rb +31 -0
  40. data/tests/tc_curl_multi.rb +485 -0
  41. data/tests/tc_curl_postfield.rb +143 -0
  42. data/tests/timeout.rb +100 -0
  43. data/tests/timeout_server.rb +33 -0
  44. data/tests/unittests.rb +2 -0
  45. metadata +133 -0
data/LICENSE ADDED
@@ -0,0 +1,51 @@
1
+ Copyright (c) 2006 Ross Bamford (rosco AT roscopeco DOT co DOT uk).
2
+ Curb is free software licensed under the following terms:
3
+
4
+ 1. You may make and give away verbatim copies of the source form of the
5
+ software without restriction, provided that you duplicate all of the
6
+ original copyright notices and associated disclaimers.
7
+
8
+ 2. You may modify your copy of the software in any way, provided that
9
+ you do at least ONE of the following:
10
+
11
+ a) place your modifications in the Public Domain or otherwise
12
+ make them Freely Available, such as by posting said
13
+ modifications to Usenet or an equivalent medium, or by allowing
14
+ the author to include your modifications in the software.
15
+
16
+ b) use the modified software only within your corporation or
17
+ organization.
18
+
19
+ c) give non-standard binaries non-standard names, with
20
+ instructions on where to get the original software distribution.
21
+
22
+ d) make other distribution arrangements with the author.
23
+
24
+ 3. You may distribute the software in object code or binary form,
25
+ provided that you do at least ONE of the following:
26
+
27
+ a) distribute the binaries and library files of the software,
28
+ together with instructions (in the manual page or equivalent)
29
+ on where to get the original distribution.
30
+
31
+ b) accompany the distribution with the machine-readable source of
32
+ the software.
33
+
34
+ c) give non-standard binaries non-standard names, with
35
+ instructions on where to get the original software distribution.
36
+
37
+ d) make other distribution arrangements with the author.
38
+
39
+ 4. You may modify and include the part of the software into any other
40
+ software (possibly commercial).
41
+
42
+ 5. The scripts and library files supplied as input to or produced as
43
+ output from the software do not automatically fall under the
44
+ copyright of the software, but belong to whomever generated them,
45
+ and may be sold commercially, and may be aggregated with this
46
+ software.
47
+
48
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
49
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
50
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51
+ PURPOSE.
data/README ADDED
@@ -0,0 +1,194 @@
1
+ # Curb - Libcurl bindings for Ruby
2
+
3
+ + [rubyforge rdoc](http://curb.rubyforge.org/)
4
+ + [rubyforge project](http://rubyforge.org/projects/curb)
5
+ + [github project](http://github.com/taf2/curb/tree/master)
6
+
7
+ Curb (probably CUrl-RuBy or something) provides Ruby-language bindings for the
8
+ libcurl(3), a fully-featured client-side URL transfer library.
9
+ cURL and libcurl live at [http://curl.haxx.se/](http://curl.haxx.se/) .
10
+
11
+ Curb is a work-in-progress, and currently only supports libcurl's 'easy' and 'multi' modes.
12
+
13
+ ## License
14
+
15
+ Curb is copyright (c)2006 Ross Bamford, and released under the terms of the
16
+ Ruby license. See the LICENSE file for the gory details.
17
+
18
+ ## You will need
19
+
20
+ + A working Ruby installation (1.8+, tested with 1.8.6, 1.8.7, 1.9.1, and 1.9.2)
21
+ + A working (lib)curl installation, with development stuff (7.5+, tested with 7.19.x)
22
+ + A sane build environment (e.g. gcc, make)
23
+
24
+ ## Installation...
25
+
26
+ ... will usually be as simple as:
27
+
28
+ $ gem install curb
29
+
30
+ Or, if you downloaded the archive:
31
+
32
+ $ rake install
33
+
34
+ If you have a wierd setup, you might need extconf options. In this case, pass
35
+ them like so:
36
+
37
+ $ rake install EXTCONF_OPTS='--with-curl-dir=/path/to/libcurl --prefix=/what/ever'
38
+
39
+ Curb is tested only on GNU/Linux x86 and Mac OSX - YMMV on other platforms.
40
+ If you do use another platform and experience problems, or if you can
41
+ expand on the above instructions, please report the issue at http://github.com/taf2/curb/issues
42
+
43
+ On Ubuntu, the dependencies can be satisfied by installing the following packages:
44
+
45
+ $ sudo apt-get install libcurl3 libcurl3-gnutls libcurl4-openssl-dev
46
+
47
+ Curb has fairly extensive RDoc comments in the source. You can build the
48
+ documentation with:
49
+
50
+ $ rake doc
51
+
52
+ ## Usage & examples
53
+
54
+ Curb provides two classes:
55
+
56
+ + Curl::Easy - simple API, for day-to-day tasks.
57
+ + Curl::Multi - more advanced API, for operating on multiple URLs simultaneously.
58
+
59
+ ### Super simple API (less typing)
60
+
61
+ http = Curl.get("http://www.google.com/")
62
+ puts http.body_str
63
+
64
+ http = Curl.post("http://www.google.com/", {:foo => "bar"})
65
+ puts http.body_str
66
+
67
+ http = Curl.get("http://www.google.com/") do|http|
68
+ http.headers['Cookie'] = 'foo=1;bar=2'
69
+ end
70
+ puts http.body_str
71
+
72
+ ### Simple fetch via HTTP:
73
+
74
+ c = Curl::Easy.perform("http://www.google.co.uk")
75
+ puts c.body_str
76
+
77
+ Same thing, more manual:
78
+
79
+ c = Curl::Easy.new("http://www.google.co.uk")
80
+ c.perform
81
+ puts c.body_str
82
+
83
+ ### Additional config:
84
+
85
+ Curl::Easy.perform("http://www.google.co.uk") do |curl|
86
+ curl.headers["User-Agent"] = "myapp-0.0"
87
+ curl.verbose = true
88
+ end
89
+
90
+ Same thing, more manual:
91
+
92
+ c = Curl::Easy.new("http://www.google.co.uk") do |curl|
93
+ curl.headers["User-Agent"] = "myapp-0.0"
94
+ curl.verbose = true
95
+ end
96
+
97
+ c.perform
98
+
99
+ ### HTTP basic authentication:
100
+
101
+ c = Curl::Easy.new("http://github.com/")
102
+ c.http_auth_types = :basic
103
+ c.username = 'foo'
104
+ c.password = 'bar'
105
+ c.perform
106
+
107
+ ### Supplying custom handlers:
108
+
109
+ c = Curl::Easy.new("http://www.google.co.uk")
110
+
111
+ c.on_body { |data| print(data) }
112
+ c.on_header { |data| print(data) }
113
+
114
+ c.perform
115
+
116
+ ### Reusing Curls:
117
+
118
+ c = Curl::Easy.new
119
+
120
+ ["http://www.google.co.uk", "http://www.ruby-lang.org/"].map do |url|
121
+ c.url = url
122
+ c.perform
123
+ c.body_str
124
+ end
125
+
126
+ ### HTTP POST form:
127
+
128
+ c = Curl::Easy.http_post("http://my.rails.box/thing/create",
129
+ Curl::PostField.content('thing[name]', 'box'),
130
+ Curl::PostField.content('thing[type]', 'storage'))
131
+
132
+ ### HTTP POST file upload:
133
+
134
+ c = Curl::Easy.new("http://my.rails.box/files/upload")
135
+ c.multipart_form_post = true
136
+ c.http_post(Curl::PostField.file('thing[file]', 'myfile.rb'))
137
+
138
+ ### Multi Interface (Basic HTTP GET):
139
+
140
+ # make multiple GET requests
141
+ easy_options = {:follow_location => true}
142
+ multi_options = {:pipeline => true}
143
+
144
+ Curl::Multi.get('url1','url2','url3','url4','url5', easy_options, multi_options) do|easy|
145
+ # do something interesting with the easy response
146
+ puts easy.last_effective_url
147
+ end
148
+
149
+ ### Multi Interface (Basic HTTP POST):
150
+
151
+ # make multiple POST requests
152
+ easy_options = {:follow_location => true, :multipart_form_post => true}
153
+ multi_options = {:pipeline => true}
154
+
155
+ url_fields = [
156
+ { :url => 'url1', :post_fields => {'f1' => 'v1'} },
157
+ { :url => 'url2', :post_fields => {'f1' => 'v1'} },
158
+ { :url => 'url3', :post_fields => {'f1' => 'v1'} }
159
+ ]
160
+
161
+ Curl::Multi.post(url_fields, easy_options, multi_options) do|easy|
162
+ # do something interesting with the easy response
163
+ puts easy.last_effective_url
164
+ end
165
+
166
+ ### Multi Interface (Advanced):
167
+
168
+ responses = {}
169
+ requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"]
170
+ m = Curl::Multi.new
171
+ # add a few easy handles
172
+ requests.each do |url|
173
+ responses[url] = ""
174
+ c = Curl::Easy.new(url) do|curl|
175
+ curl.follow_location = true
176
+ curl.on_body{|data| responses[url] << data; data.size }
177
+ curl.on_success {|easy| puts "success, add more easy handles" }
178
+ end
179
+ m.add(c)
180
+ end
181
+
182
+ m.perform do
183
+ puts "idling... can do some work here"
184
+ end
185
+
186
+ requests.each do|url|
187
+ puts responses[url]
188
+ end
189
+
190
+ ### Easy Callbacks
191
+
192
+ on_success: is called when the response code is 20x
193
+ on_failure: is called when the response code is not success, including redirects e.g. 30x
194
+ on_complete: is called in all cases.
data/Rakefile ADDED
@@ -0,0 +1,320 @@
1
+ # $Id$
2
+ #
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ begin
6
+ require 'rdoc/task'
7
+ rescue LoadError => e
8
+ require 'rake/rdoctask'
9
+ end
10
+
11
+ CLEAN.include '**/*.o'
12
+ CLEAN.include "**/*.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}"
13
+ CLOBBER.include 'doc'
14
+ CLOBBER.include '**/*.log'
15
+ CLOBBER.include '**/Makefile'
16
+ CLOBBER.include '**/extconf.h'
17
+
18
+ def announce(msg='')
19
+ $stderr.puts msg
20
+ end
21
+
22
+ desc "Default Task (Test project)"
23
+ task :default => :test
24
+
25
+ # Determine the current version of the software
26
+ if File.read('ext/curb.h') =~ /\s*CURB_VERSION\s*['"](\d.+)['"]/
27
+ CURRENT_VERSION = $1
28
+ else
29
+ CURRENT_VERSION = "0.0.0"
30
+ end
31
+
32
+ if ENV['REL']
33
+ PKG_VERSION = ENV['REL']
34
+ else
35
+ PKG_VERSION = CURRENT_VERSION
36
+ end
37
+
38
+ task :test_ver do
39
+ puts PKG_VERSION
40
+ end
41
+
42
+ # Make tasks -----------------------------------------------------
43
+ make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
44
+ MAKECMD = ENV['MAKE_CMD'] || make_program
45
+ MAKEOPTS = ENV['MAKE_OPTS'] || ''
46
+
47
+ CURB_SO = "ext/curb_core.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}"
48
+
49
+ file 'ext/Makefile' => 'ext/extconf.rb' do
50
+ Dir.chdir('ext') do
51
+ ruby "extconf.rb #{ENV['EXTCONF_OPTS']}"
52
+ end
53
+ end
54
+
55
+ def make(target = '')
56
+ Dir.chdir('ext') do
57
+ pid = system("#{MAKECMD} #{MAKEOPTS} #{target}")
58
+ $?.exitstatus
59
+ end
60
+ end
61
+
62
+ # Let make handle dependencies between c/o/so - we'll just run it.
63
+ file CURB_SO => (['ext/Makefile'] + Dir['ext/*.c'] + Dir['ext/*.h']) do
64
+ m = make
65
+ fail "Make failed (status #{m})" unless m == 0
66
+ end
67
+
68
+ desc "Compile the shared object"
69
+ task :compile => [CURB_SO]
70
+
71
+ desc "Create the markdown file"
72
+ task :markdown do
73
+ cp "README", "README.markdown"
74
+ end
75
+
76
+ desc "Install to your site_ruby directory"
77
+ task :install => :alltests do
78
+ m = make 'install'
79
+ fail "Make install failed (status #{m})" unless m == 0
80
+ end
81
+
82
+ # Test Tasks ---------------------------------------------------------
83
+ task :ta => :alltests
84
+ task :tu => :unittests
85
+ task :test => [:rmpid,:unittests]
86
+
87
+ task :rmpid do
88
+ FileUtils.rm_rf Dir.glob("tests/server_lock-*")
89
+ end
90
+
91
+ if ENV['RELTEST']
92
+ announce "Release task testing - not running regression tests on alltests"
93
+ task :alltests => [:unittests]
94
+ else
95
+ task :alltests => [:unittests, :bugtests]
96
+ end
97
+
98
+ Rake::TestTask.new(:unittests) do |t|
99
+ t.test_files = FileList['tests/tc_*.rb']
100
+ t.verbose = false
101
+ end
102
+
103
+ Rake::TestTask.new(:bugtests) do |t|
104
+ t.test_files = FileList['tests/bug_*.rb']
105
+ t.verbose = false
106
+ end
107
+
108
+ #Rake::TestTask.new(:funtests) do |t|
109
+ # t.test_files = FileList['test/func_*.rb']
110
+ #t.warning = true
111
+ #t.warning = true
112
+ #end
113
+
114
+ task :unittests => :compile
115
+ task :bugtests => :compile
116
+
117
+ def has_gem?(file,name)
118
+ begin
119
+ require file
120
+ has_http_persistent = true
121
+ rescue LoadError => e
122
+ puts "Skipping #{name}"
123
+ end
124
+ end
125
+
126
+ desc "Benchmark curl against http://127.0.0.1/zeros-2k - will fail if /zeros-2k or 127.0.0.1 are missing"
127
+ task :bench do
128
+ sh "ruby bench/curb_easy.rb"
129
+ sh "ruby bench/curb_multi.rb"
130
+ sh "ruby bench/nethttp_test.rb" if has_gem?("net/http/persistent","net-http-persistent")
131
+ sh "ruby bench/patron_test.rb" if has_gem?("patron","patron")
132
+ sh "ruby bench/typhoeus_test.rb" if has_gem?("typhoeus","typhoeus")
133
+ sh "ruby bench/typhoeus_hydra_test.rb" if has_gem?("typhoeus","typhoeus")
134
+ end
135
+
136
+ # RDoc Tasks ---------------------------------------------------------
137
+ desc "Create the RDOC documentation"
138
+ task :doc do
139
+ ruby "doc.rb #{ENV['DOC_OPTS']}"
140
+ end
141
+
142
+ desc "Publish the RDoc documentation to project web site"
143
+ task :doc_upload => [ :doc ] do
144
+ if ENV['RELTEST']
145
+ announce "Release Task Testing, skipping doc upload"
146
+ else
147
+ unless ENV['RUBYFORGE_ACCT']
148
+ raise "Need to set RUBYFORGE_ACCT to your rubyforge.org user name (e.g. 'fred')"
149
+ end
150
+
151
+ require 'rake/contrib/sshpublisher'
152
+ Rake::SshDirPublisher.new(
153
+ "#{ENV['RUBYFORGE_ACCT']}@rubyforge.org",
154
+ "/var/www/gforge-projects/curb",
155
+ "doc"
156
+ ).upload
157
+ end
158
+ end
159
+
160
+ if ! defined?(Gem)
161
+ warn "Package Target requires RubyGEMs"
162
+ else
163
+ desc 'Generate gem specification'
164
+ task :gemspec do
165
+ require 'erb'
166
+ tspec = ERB.new(File.read(File.join(File.dirname(__FILE__),'lib','curb.gemspec.erb')))
167
+ File.open(File.join(File.dirname(__FILE__),'curb.gemspec'),'wb') do|f|
168
+ f << tspec.result
169
+ end
170
+ end
171
+
172
+ desc 'Build gem'
173
+ task :package => :gemspec do
174
+ require 'rubygems/specification'
175
+ spec_source = File.read File.join(File.dirname(__FILE__),'curb.gemspec')
176
+ spec = nil
177
+ # see: http://gist.github.com/16215
178
+ Thread.new { spec = eval("$SAFE = 3\n#{spec_source}") }.join
179
+ spec.validate
180
+ Gem::Builder.new(spec).build
181
+ end
182
+
183
+ task :static do
184
+ ENV['STATIC_BUILD'] = '1'
185
+ end
186
+
187
+ task :binary_gemspec => [:static, :compile] do
188
+ require 'erb'
189
+ ENV['BINARY_PACKAGE'] = '1'
190
+ tspec = ERB.new(File.read(File.join(File.dirname(__FILE__),'lib','curb.gemspec.erb')))
191
+
192
+ File.open(File.join(File.dirname(__FILE__),'curb-binary.gemspec'),'wb') do|f|
193
+ f << tspec.result
194
+ end
195
+ end
196
+
197
+ desc 'Strip extra strings from Binary'
198
+ task :binary_strip do
199
+ strip = '/usr/bin/strip'
200
+ if File.exist?(strip) and `#{strip} -h 2>&1`.match(/GNU/)
201
+ sh "#{strip} #{CURB_SO}"
202
+ end
203
+ end
204
+
205
+ desc 'Build gem'
206
+ task :binary_package => [:binary_gemspec, :binary_strip] do
207
+ require 'rubygems/specification'
208
+ spec_source = File.read File.join(File.dirname(__FILE__),'curb-binary.gemspec')
209
+ spec = nil
210
+ # see: http://gist.github.com/16215
211
+ Thread.new { spec = eval("$SAFE = 3\n#{spec_source}") }.join
212
+ spec.validate
213
+ Gem::Builder.new(spec).build
214
+ end
215
+ end
216
+
217
+ # --------------------------------------------------------------------
218
+ # Creating a release
219
+ desc "Make a new release (Requires SVN commit / webspace access)"
220
+ task :release => [
221
+ :prerelease,
222
+ :clobber,
223
+ :alltests,
224
+ :update_version,
225
+ :package,
226
+ :tag,
227
+ :doc_upload] do
228
+
229
+ announce
230
+ announce "**************************************************************"
231
+ announce "* Release #{PKG_VERSION} Complete."
232
+ announce "* Packages ready to upload."
233
+ announce "**************************************************************"
234
+ announce
235
+ end
236
+
237
+ # Validate that everything is ready to go for a release.
238
+ task :prerelease do
239
+ announce
240
+ announce "**************************************************************"
241
+ announce "* Making RubyGem Release #{PKG_VERSION}"
242
+ announce "* (current version #{CURRENT_VERSION})"
243
+ announce "**************************************************************"
244
+ announce
245
+
246
+ # Is a release number supplied?
247
+ unless ENV['REL']
248
+ fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
249
+ end
250
+
251
+ # Is the release different than the current release.
252
+ # (or is REUSE set?)
253
+ if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE']
254
+ fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version"
255
+ end
256
+
257
+ # Are all source files checked in?
258
+ if ENV['RELTEST']
259
+ announce "Release Task Testing, skipping checked-in file test"
260
+ else
261
+ announce "Checking for unchecked-in files..."
262
+ data = `svn status`
263
+ unless data =~ /^$/
264
+ fail "SVN status is not clean ... do you have unchecked-in files?"
265
+ end
266
+ announce "No outstanding checkins found ... OK"
267
+ end
268
+
269
+ announce "Doc will try to use GNU cpp if available"
270
+ ENV['DOC_OPTS'] = "--cpp"
271
+ end
272
+
273
+ # Used during release packaging if a REL is supplied
274
+ task :update_version do
275
+ unless PKG_VERSION == CURRENT_VERSION
276
+ pkg_vernum = PKG_VERSION.tr('.','').sub(/^0*/,'')
277
+ pkg_vernum << '0' until pkg_vernum.length > 2
278
+
279
+ File.open('ext/curb.h.new','w+') do |f|
280
+ maj, min, mic, patch = /(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?/.match(PKG_VERSION).captures
281
+ f << File.read('ext/curb.h').
282
+ gsub(/CURB_VERSION\s+"(\d.+)"/) { "CURB_VERSION \"#{PKG_VERSION}\"" }.
283
+ gsub(/CURB_VER_NUM\s+\d+/) { "CURB_VER_NUM #{pkg_vernum}" }.
284
+ gsub(/CURB_VER_MAJ\s+\d+/) { "CURB_VER_MAJ #{maj}" }.
285
+ gsub(/CURB_VER_MIN\s+\d+/) { "CURB_VER_MIN #{min}" }.
286
+ gsub(/CURB_VER_MIC\s+\d+/) { "CURB_VER_MIC #{mic || 0}" }.
287
+ gsub(/CURB_VER_PATCH\s+\d+/) { "CURB_VER_PATCH #{patch || 0}" }
288
+ end
289
+ mv('ext/curb.h.new', 'ext/curb.h')
290
+ if ENV['RELTEST']
291
+ announce "Release Task Testing, skipping commiting of new version"
292
+ else
293
+ sh %{svn commit -m "Updated to version #{PKG_VERSION}" ext/curb.h}
294
+ end
295
+ end
296
+ end
297
+
298
+ # "Create a new SVN tag with the latest release number (REL=x.y.z)"
299
+ task :tag => [:prerelease] do
300
+ reltag = "curb-#{PKG_VERSION}"
301
+ reltag << ENV['REUSE'] if ENV['REUSE']
302
+ announce "Tagging SVN with [#{reltag}]"
303
+ if ENV['RELTEST']
304
+ announce "Release Task Testing, skipping SVN tagging"
305
+ else
306
+ # need to get current base URL
307
+ s = `svn info`
308
+ if s =~ /URL:\s*([^\n]*)\n/
309
+ svnroot = $1
310
+ if svnroot =~ /^(.*)\/trunk/i
311
+ svnbase = $1
312
+ sh %{svn cp #{svnroot} #{svnbase}/TAGS/#{reltag} -m "Release #{PKG_VERSION}"}
313
+ else
314
+ fail "Please merge to trunk before making a release"
315
+ end
316
+ else
317
+ fail "Unable to determine repository URL from 'svn info' - is this a working copy?"
318
+ end
319
+ end
320
+ end