File: pkg-ruby-get-sources

package info (click to toggle)
ruby-pkg-tools 0.11.4
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 108 kB
  • ctags: 49
  • sloc: ruby: 246; perl: 15; sh: 5; makefile: 3
file content (330 lines) | stat: -rwxr-xr-x 9,740 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#!/usr/bin/ruby

# pkg-ruby-get-sources - downloads sources of Debian ruby extra packages
# Copyright (C) 2005 Antonio S. de A. Terceiro <asaterceiro@inf.ufrgs.br>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

# TODO: check for already downloaded files (and their size, maybe)
# TODO: delete temporary bzipped archives after converting to gzipped

require 'uri'
require 'net/http'
require 'yaml'
require 'getoptlong'
require 'open-uri'
require 'rexml/document'

$sources_file = "/usr/share/ruby-pkg-tools/pkg-ruby-extras.sources"
$sources_url = "http://pkg-ruby-extras.alioth.debian.org/pkg-ruby-extras.sources"
$watch_file = 'debian/watch'
$target_directory = "../tarballs"
$verbose = false
$list_available = false

def info_msg(text)
  puts "I: #{text}"
end

def error_msg(text)
  puts "E: #{text}"
end

opts = GetoptLong.new(
  [ "--sources",           "-s",  GetoptLong::REQUIRED_ARGUMENT ],
  [ "--target-directory",  "-t",  GetoptLong::REQUIRED_ARGUMENT ],
  [ "--verbose",           "-v",  GetoptLong::NO_ARGUMENT ],
  [ "--list-available",    "-l",  GetoptLong::NO_ARGUMENT ],
  [ "--help",              "-h",  GetoptLong::NO_ARGUMENT ],
  [ "--version",                  GetoptLong::NO_ARGUMENT ]
)

help = {
  "--sources" => "indicates the sources file (defaults to #{$sources_file})",
  "--target-directory" => "indicates where to put downloaded tarballs (defaults to #{$target_directory})",
  "--verbose" => "display verbose information while running",
  "--list-available" => "does not download any file; only list available packages and versions",
  "--help" => "shows this help message",
  "--version" => "shows version information and exit"
}

# maps extensions of upstream tarballs to extensions of the downloaded
# archives
$extensions = {
  # rename .tgz to .tar.gz; dpkg-source likes it that way
  'tgz' => 'tar.gz',
  # do not rename these archive types
  'tar.gz' => nil,
  'tar.bz2' => nil
}

begin
  opts.each { |opt,val|
    case opt
      when '--verbose'
        $verbose = true
      when '--sources'
        $sources_file = val
      when '--target-directory'
        $target_directory = val
      when '--help'
        puts "Usage: #{$PROGRAM_NAME} [options]"
        puts "Options:"
        help.each { |optname,helpmsg|
          puts(format("  %-20s %s\n",optname,helpmsg))
        }
        exit
      when '--version'
        puts "#{$PROGRAM_NAME}, version #{0.1}"
        exit
      when '--list-available'
        $list_available = true
    end
  }
rescue
  exit 1
end

info_msg("Running in verbose mode") if $verbose
info_msg("Reading sources from #{$sources_url}, #{$sources_file}") if $verbose
info_msg("Downloading files to #{$target_directory}") if $verbose

if (!$list_available and ! (File.directory?($target_directory) and File.writable?($target_directory)))
  error_msg("#{$target_directory} should be a valid writable directory")
  exit 1
end

# guess package name and version from debian/changelog
# ex: liblocale-ruby (0.1-2)
def guess_package()
  info_msg("Trying to guess package name and version from debian/changelog ... ") if $verbose
  if !File.readable?("debian/changelog")
    error_msg("Couldn't read debian/changelog")
    exit 2
  end

  # guess a package's name and version from debian/changelog
  changelogline = File.read("debian/changelog").split(/\n/)[0]
  matches = /^(\S+)\s*\(([^-]+)-([^-]+)\)/.match(changelogline)
  package = matches[1]
  version = matches[2]
  info_msg("Package: #{package}") if $verbose
  info_msg("Version: #{version}") if $verbose

  return [ package, version ]
end

# reads a YAML object from a given sources file
def get_available_sources(source_file)
  info_msg("Loading available sources from #{source_file} ... ") if $verbose
  begin
    sources = open(source_file) { |io| YAML::load(io) }
  rescue Exception => exception
    error_msg("#{exception}") if $verbose
    return false
  end

  return sources
end

# list available packages from a sources file
def list_packages(sources)
  sources.each { |package,versions|
    versions.each { |version,tarball_url|
      puts "#{package}-#{version}"
    }
  }
end

# follows redirections up to <limit> levels deep
def follow(uri, limit = 10)
  # anyone know what the second parameter is supposed to do? HTTP status code?
  raise Net::HTTPFatalError.new('Too many redirections', 0) if limit == 0

  begin
    response = Net::HTTP.get_response(URI.parse(uri))
  rescue SocketError => error
    error_msg("#{error}")
    return false
  end

  case response
    when Net::HTTPSuccess then
      return uri
    when Net::HTTPRedirection then
      info_msg("Redirecting: #{response['location']}") if $verbose
      return follow(response['location'], limit-1)
    else
      raise Net::HTTPFatalError.new(response.message, response.code)
  end
end

# downloads a given package from a specified URL
def download(package,version,tarball_url)
  # remove any leading or trailing whitespace (including \n) which would
  # otherwise ruin the nice progress display
  tarball_url.strip!

  # follow redirection
  begin
    tarball_url = follow(tarball_url)
  rescue Net::HTTPFatalError => error
    error_msg("HTTP Error: #{error}")
    return false
  end

  if tarball_url then
    uri = URI.parse(tarball_url)
  else
    return false
  end

  info_msg("URL: #{tarball_url}") if $verbose
  
  source = uri.path.split('/').pop
  ext = nil

  $extensions.each { |key, val|
    if source.index(key) then
      ext = val ? val : key
      info_msg("Known archive format: #{source}") if $verbose
    end
  }
  
  if not ext then
    error_msg("Unknown archive type: #{uri.path}")
    return false
  end

  tarball_name = "#{package}_#{version}.orig.#{ext}"
  filename = File.join($target_directory,tarball_name)

  Net::HTTP.start(uri.host, uri.port) { |http|
    http.request_get(uri.path) { |res|
      total = res.header.content_length
      current = 0
      File.open(filename,'w') { |file|
        res.read_body { |part|
          current += part.length
          print(format("\r[%3d%%] #{tarball_url}",((current * 100) / total)))
          $stdout.flush
          file.write(part)
        }
        puts
      }
      info_msg("Saved #{filename}") if $verbose
    }
  }

  orig_tarball = filename.sub(ext, 'tar.gz')

  case ext
    when 'tar.bz2'
      puts "I: Converting #{filename} to #{orig_tarball}" if $verbose
      system "bzcat '#{filename}' | gzip -c > '#{orig_tarball}'"
    else
      info_msg("Nothing to do for archive type: #{ext}") if $verbose
  end

  return true
end

package, version = guess_package
tarball = nil

# first try to download a source tarball by looking at the watch file, if the
# package has one
begin
  File.readlines($watch_file).each { |line|
    if line =~ /(http|ftp):\/\/\S+/ then
      tarball = Regexp.last_match(0)
      tarball.gsub!(/\\/, '')
      tarball.gsub!(/\(.*\)/, version)

      if not $list_available then
        if tarball then
          info_msg("Found source tarball for package #{package}-#{version}: #{tarball}") if $verbose
          if download(package,version,tarball) then
            exit 0
          end
        else
          info_msg("Found no source tarball by looking at the watch file, continuing") if $verbose
        end
      end
    end
  }
rescue
  info_msg("No watch file for this package") if $verbose
end

# Now try to use the output of uscan --dehs to get a source tarball URL
if File.exist?($watch_file)
  begin
    info_msg("Running uscan --dehs and parsing the output ...") if $verbose
    i = IO::popen('uscan --dehs')
    d = REXML::Document::new(i.read + "</dehs>")
    i.close
    v = d.elements['/dehs/upstream-version'].text
    if v == version
      tarball = d.elements['/dehs/upstream-url'].text
      info_msg("Found source tarball for package #{package}-#{version}: #{tarball}") if $verbose
      if download(package,version,tarball) then
        exit 0
      end
    else
      info_msg("uscan --dehs only gave us a tarball for v. #{v}, while we were looking for #{version}") if $verbose
    end
  rescue
    info_msg("The uscan --dehs strategy failed") if $verbose
  end
end

# Try to get the source URL from a sources file
[ $sources_file, $sources_url ].each { |file|
  sources = get_available_sources(file)
  if (sources) then
    if $list_available then
      list_packages(sources)
    else
      if sources[package] then
        if sources[package][version] then
          tarball = sources[package][version]
          break
        else
          error_msg("#{package}'s version #{version} is not available (in #{file}).")
        end
      else
        error_msg("Package #{package} is not available (in #{file}).")
        next
      end
    end
  end
}

# don't go beyond here if all we wanted to do was list available packages
exit if $list_available

if tarball then
  info_msg("Found source tarball for package #{package}-#{version}: #{tarball}") if $verbose
else
  error_msg("No source tarball found for package #{package}-#{version}")
  exit 2
end

# actually download package
download(package,version,tarball)

# vi: sts=2 sw=2 ts=2 et