Package: devscripts
Version: 2.10.7
Severity: wishlist

Hi,

I'd like to propose a new script for devscripts. It's a rewrite of what
used to be known as 'MultiDistroTools' (or mdt). Its use is to create
'APT trees' for several distributions, making it easy to query the
status of packages in Ubuntu without using chroots, for example.

Example usage:
------------------------------------------------------
[EMAIL PROTECTED]:~$ chdist create sid
Now edit /home/lucas/.chdist/sid/etc/apt/sources.list
Then run chdist apt-get sid update
And enjoy.
[EMAIL PROTECTED]:~$ vi /home/lucas/.chdist/sid/etc/apt/sources.list
[EMAIL PROTECTED]:~$ chdist create gutsy
Now edit /home/lucas/.chdist/gutsy/etc/apt/sources.list
Then run chdist apt-get gutsy update
And enjoy.
[EMAIL PROTECTED]:~$ vi /home/lucas/.chdist/gutsy/etc/apt/sources.list
[EMAIL PROTECTED]:~$ chdist apt-get sid update
Get:1 http://ftp.debian.org unstable Release.gpg [189B]
Ign http://ftp.debian.org unstable/main Translation-en_US
[...]
[EMAIL PROTECTED]:~$ chdist apt-get gutsy update
Get:1 http://archive.ubuntu.com gutsy Release.gpg [191B]
[...]
[EMAIL PROTECTED]:~$ chdist apt-cache gutsy show devscripts | head -10
Package: devscripts
Priority: optional
Section: devel
Installed-Size: 1208
Maintainer: Ubuntu Core Developers
<[EMAIL PROTECTED]>
Original-Maintainer: Devscripts Devel Team
<[EMAIL PROTECTED]>
Architecture: i386
Version: 2.10.7ubuntu1
Depends: dpkg-dev, debianutils (>= 2.0), perl (>= 5.8), sed (>= 2.95),
libc6 (>= 2.6-1)
Recommends: fakeroot
[EMAIL PROTECTED]:~$ chdist compare-packages sid gutsy |grep "^dev"
devede UNAVAIL 2.13-0.0
devel-protocols UNAVAIL 1.5
develock-el 0.34-2 0.34-2
developers-reference 3.3.8 3.3.8
devhelp 0.15-1 0.15-0ubuntu1
device-tree-compiler UNAVAIL 0.1-1
device3dfx 2007.02.06-1 2007.02.06-1
devil 1.6.7-5 1.6.7-5
devilspie 0.20.2-1 0.20.2-1build1
devio 1.2-1 1.2-1
devmapper 2:1.02.20-2 2:1.02.20-1ubuntu3
devscripts 2.10.7 2.10.7ubuntu1
devtodo 0.1.19-3 0.1.19-3
[EMAIL PROTECTED]:~$ chdist compare-versions sid gutsy |grep "^dev"
devede UNAVAIL 2.13-0.0 not_in_sid
devel-protocols UNAVAIL 1.5 not_in_sid
develock-el 0.34-2 0.34-2 same_version
developers-reference 3.3.8 3.3.8 same_version
devhelp 0.15-1 0.15-0ubuntu1 newer_in_sid
device-tree-compiler UNAVAIL 0.1-1 not_in_sid
device3dfx 2007.02.06-1 2007.02.06-1 same_version
devil 1.6.7-5 1.6.7-5 same_version
devilspie 0.20.2-1 0.20.2-1build1 newer_in_gutsy
devio 1.2-1 1.2-1 same_version
devmapper 2:1.02.20-2 2:1.02.20-1ubuntu3 newer_in_sid
devscripts 2.10.7 2.10.7ubuntu1 newer_in_gutsy
devtodo 0.1.19-3 0.1.19-3 same_version
------------------------------------------------------

The main problem with my script is that it's written in ruby, and I
would really hate to rewrite it in perl just to include it in
devscripts. I've seen that so far, you only have sh and perl scripts. Do
you have a policy against adding scripts in other languages?

I've attached the script in question. I'll work on a clean patch if you
agree on its inclusion despite being in ruby.

Thank you,
-- 
| Lucas Nussbaum
| [EMAIL PROTECTED]   http://www.lucas-nussbaum.net/ |
| jabber: [EMAIL PROTECTED]             GPG: 1024D/023B3F4F |
#!/usr/bin/ruby -w

require 'fileutils'
require 'getoptlong'

$datadir = ENV['HOME'] + '/.chdist'

def usage
  return <<-EOF
Usage: chdist [options] [command] [command parameters]

Options:
    -h, --help                       Show this help
    -d, --data-dir DIR               Choose data directory (default: 
$HOME/.chdist/

Commands:
  create DIST : prepare a new tree named DIST
  apt-get DIST (update|source|...) : run apt-get inside DIST
  apt-cache DIST (show|showsrc|...) : run apt-cache inside DIST
  apt-rdepends DIST [...] : run apt-rdepends inside DIST
  src2bin DIST PKG : get binary packages for a source package in DIST
  bin2src DIST PKG : get source package for a binary package in DIST
  compare-packages DIST1 DIST2 [DIST3, ...] : list versions of packages in
      several DISTributions
  compare-versions DIST1 DIST2 : same as compare-packages, but also run
      dpkg --compare-versions and display where the package is newer
  grep-dctrl-packages DIST [...] : run grep-dctrl on *_Packages inside DIST
  grep-dctrl-sources DIST [...] : run grep-dctrl on *_Sources inside DIST
  EOF
end

# specify the options we accept and initialize
# the option parser
opts = GetoptLong.new(
  [ "--help",    "-h",            GetoptLong::NO_ARGUMENT ],
  [ "--data-dir", "-d",           GetoptLong::REQUIRED_ARGUMENT ]
)
opts.ordering = GetoptLong::REQUIRE_ORDER

# process the parsed options
opts.each do |opt, arg|
  if opt == '--help'
    puts usage
    exit(0)
  elsif opt == '--data-dir'
    $datadir = arg
  end
end

if ARGV.length == 0
  puts usage
  exit(1)
end

########################################################
def dist_check(dist)
  dir = $datadir + '/' + dist
  return true if File::directory?(dir)
  puts "Could not find #{dist} in $datadir. Exiting."
  exit(1)
end

def aptopts(dist)
  return ['-o',"Dir=#{$datadir}/#{dist}/", '-o', 
"Dir::State::status=#{$datadir}/#{dist}/var/lib/dpkg/status"]
end

def compare_versions(va, vb)
  return IO::popen("dpkg --compare-versions #{va} lt #{vb}; echo $?").read.to_i 
== 1
end

###

def aptcache(args)
  dist_check(dist = args.shift)
  args = aptopts(dist) + args
  exec('/usr/bin/apt-cache', *args)
end

def aptget(args)
  dist_check(dist = args.shift)
  args = aptopts(dist) + args
  exec('/usr/bin/apt-get', *args)
end

def aptrdepends(args)
  dist_check(dist = args.shift)
  args = aptopts(dist) + args
  exec('/usr/bin/apt-rdepends', *args)
end

def bin2src(args)
  dist_check(dist = args[0])
  args = aptopts(dist) + ['show', args[1] ]
  cmd = (['/usr/bin/apt-cache'] + args).join(' ')
  # FIXME avoid that ugly shell invocation
  lines = `#{cmd}`
  if $?.exitstatus != 0
    exit($?.exitstatus)
  end
  source = lines.split(/\n/).grep(/^Source: /)
  if source == []
    puts args[1]
  else
    puts source[0].split(' ', 2)[1]
  end
end

def src2bin(argv)
  dist_check(dist = argv[0])
  args = aptopts(dist) + ['showsrc', argv[1] ]
  cmd = (['/usr/bin/apt-cache'] + args).join(' ')
  # FIXME avoid that ugly shell invocation
  lines = `#{cmd}`
  if $?.exitstatus != 0
    exit($?.exitstatus)
  end
  bins = []
  lines.split(/\n\n/).each do |lp|
    # avoid cases where apt-cache showsrc also shows source packages
    # with a binary package with that name (e.g apt-cache showsrc sm)
    src = lp.split(/\n/).grep(/^Package: /)[0].split(': ', 2)[1]
    next if src != argv[1]
    binaries = lp.split(/\n/).grep(/^Binary: /)
    if binaries != []
      binaries = binaries[0].split(/: /, 2)[1]
      bins += binaries.split(/, /)
    end
  end
  puts bins.uniq.join("\n")
end

def dist_create(argv)
  dist = argv[0]
  dir = $datadir + '/' + dist
  if File::exists?(dir)
    puts "#{dir} already exists, exiting."
    exit(1)
  end
  FileUtils::mkdir_p(dir)
  [ '/etc/apt', '/var/lib/apt/lists/partial', '/var/lib/dpkg', 
'/var/cache/apt/archives/partial'].each do |d|
    FileUtils::mkdir_p(dir + d)
  end
  File::open(dir + '/etc/apt/sources.list', 'w') do |f|
    f.puts <<-EOF
#deb http://ftp.debian.org/debian/ unstable main contrib non-free
#deb-src http://ftp.debian.org/debian/ unstable main contrib non-free

#deb http://archive.ubuntu.com/ubuntu dapper main restricted
#deb http://archive.ubuntu.com/ubuntu dapper universe multiverse
#deb-src http://archive.ubuntu.com/ubuntu dapper main restricted
#deb-src http://archive.ubuntu.com/ubuntu dapper universe multiverse
    EOF
  end
  File::open(dir + '/var/lib/dpkg/status', 'w') { |f| } # empty file
  puts "Now edit #{dir + '/etc/apt/sources.list'}"
  puts "Then run chdist apt-get #{dist} update"
  puts "And enjoy."
end

def dist_compare(argv, do_compare = false)
  dists = []
  n = 0
  # read params
  while not argv.empty? do
    a = argv.shift
    dists[n] = a
    dist_check(dists[n])
    n += 1
  end
  if do_compare and n != 2
    puts "Can only compare if there are two distros."
    exit(1)
  end
  # read Sources
  packages = {}
  dists.each do |dist|
    packages[dist] = {}
    Dir::glob($datadir + '/' + dist + '/var/lib/apt/lists/*_Sources').each do 
|f|
      pkg = nil
      IO::readlines(f).each do |l|
        if l =~ /^Package: /
          pkg = l.split(': ', 2)[1].chomp
        elsif l =~ /^Version: /
          packages[dist][pkg] = l.split(': ', 2)[1].chomp
        end
      end
    end
  end
  # output packages list
  dists.inject([]) { |tot, d| tot += packages[d].keys }.sort.uniq.each do |pkg|
    str = "#{pkg}"
    dists.each do |dist|
      if packages[dist].has_key?(pkg)
        str += " #{packages[dist][pkg]}"
      else
        str += " UNAVAIL"
      end
    end
    if do_compare
      # compare versions if run as compare-versions
      if not packages[dists[0]].has_key?(pkg)
        str += " not_in_#{dists[0]}"
      elsif not packages[dists[1]].has_key?(pkg)
        str += " not_in_#{dists[1]}"
      elsif packages[dists[0]][pkg] == packages[dists[1]][pkg]
        str += " same_version"
      elsif compare_versions(packages[dists[0]][pkg], packages[dists[1]][pkg])
        str += " newer_in_#{dists[0]}"
      else
        str += " newer_in_#{dists[1]}"
      end
    end
    puts str
  end
end

def grep_file(argv, file)
  dist = argv.shift
  dist_check(dist)
  f = Dir::glob($datadir + '/' + dist + "/var/lib/apt/lists/*_#{file}").join(' 
')
  # FIXME avoid shell invoc, potential quoting problems here
  exec("cat #{f} | grep-dctrl #{argv.join(' ')}")
end

########################################################
# Command parsing

command = ARGV.shift
case command
when 'create'
  dist_create(ARGV)
when 'apt-get' then
  aptget(ARGV)
when 'apt-cache' then
  aptcache(ARGV)
when 'apt-rdepends' then
  aptrdepends(ARGV)
when 'bin2src' then
  bin2src(ARGV)
when 'src2bin' then
  src2bin(ARGV)
when 'compare-packages' then
  dist_compare(ARGV)
when 'compare-versions' then
  dist_compare(ARGV, true)
when 'grep-dctrl-packages'
  grep_file(ARGV, 'Packages')
when 'grep-dctrl-sources'
  grep_file(ARGV, 'Sources')
else
  puts "Command unknown. Try #{$PROGRAM_NAME} -h"
  exit(1)
end

Reply via email to