#!/usr/bin/python
# Copyright (c) 2006, 2008 Canonical Ltd.
#
# 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 3 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, see .
# Originally written by Martin Pitt as 'syncpackage', and beaten up to
# cope with PPAs by Colin Watson.
import os
import sys
import urllib
import urlparse
import subprocess
import shutil
import pwd
import apt_pkg
tempdir = None
def ensure_tempdir():
global tempdir
if not tempdir:
import tempfile
import atexit
tempdir = tempfile.mkdtemp(prefix='syncpackage-ppa')
atexit.register(shutil.rmtree, tempdir)
def decompress_open(tagfile):
if tagfile.startswith('http:') or tagfile.startswith('ftp:'):
url = tagfile
tagfile = urllib.urlretrieve(url)[0]
if tagfile.endswith('.gz'):
import gzip
import tempfile
ensure_tempdir()
decompressed = tempfile.mktemp(dir=tempdir)
fin = gzip.GzipFile(filename=tagfile)
fout = open(decompressed, 'wb')
fout.write(fin.read())
fout.close()
return open(decompressed, 'r')
else:
return open(tagfile, 'r')
def tagfiletodict(tagfile):
suite = {}
tagfileobj = decompress_open(tagfile)
p = apt_pkg.ParseTagFile(tagfileobj)
while p.Step() == 1:
suite[p.Section["Package"]] = p.Section["Version"]
tagfileobj.close()
return suite
def retrieve_file(url):
'''Download file (by URL) to the current directory.
If the file is already present, this function does nothing.'''
fname = os.path.basename(url)
if not os.path.exists(fname):
print 'downloading', url
urllib.urlretrieve(url, fname)
def dsc_getfiles(dsc):
'''Return list of files in a .dsc file (excluding the .dsc file itself).'''
f = open(dsc)
files = []
# skip until 'Files:'
for l in f:
if l.strip() == 'Files:':
break
for l in f:
if not l.startswith(' '):
break
fname = l.split()[2]
if not fname.endswith('.dsc'):
files.append(fname)
f.close()
return files
#
# entry point
#
if len(sys.argv) != 4:
print 'Usage: syncpackage <.dsc URL or path> '
sys.exit(1)
apt_pkg.InitSystem()
(dscurl, release, ppa) = sys.argv[1:]
dscname = os.path.basename(dscurl)
basepath = os.path.dirname(dscurl)
(srcpkg, new_ver) = dscname.split('_')
new_ver = new_ver[:-4] # strip off '.dsc'
versions = {}
base_url = 'http://ppa.launchpad.net/%s/ubuntu/dists/%s' % (ppa, release)
release_file = decompress_open('%s/Release' % base_url)
for line in release_file:
if line.startswith('Components:'):
for component in line.split()[1:]:
versions.update(tagfiletodict('%s/%s/source/Sources.gz' % (base_url, component)))
release_file.close()
if srcpkg in versions:
cur_ver = versions[srcpkg]
else:
cur_ver = None
retrieve_file(dscurl)
files = dsc_getfiles(dscname)
# do we need the orig.tar.gz?
need_orig = True
if cur_ver is not None and cur_ver.find('-') > 0 and new_ver.find('-') > 0 and \
cur_ver.split('-')[0] == new_ver.split('-')[0]:
need_orig = False
#files = [f for f in files if not f.endswith('orig.tar.gz')]
print 'Source %s: current version %s, new version %s' % (srcpkg, cur_ver, new_ver)
print 'needs orig.tar.gz', need_orig
print 'Files:', files
for f in files:
retrieve_file(os.path.join(basepath, f))
if cur_ver is not None:
uidx = cur_ver.find('ubuntu')
if uidx > 0:
cur_ver = cur_ver[:uidx]
print 'WARNING! Overwriting modified Ubuntu version, setting current version to', cur_ver
uidx = cur_ver.find('build')
if uidx > 0:
cur_ver = cur_ver[:uidx]
orig_arg = ''
if need_orig:
orig_arg = '-sa'
# extract package, build Source
assert subprocess.call(['dpkg-source', '-x', dscname]) == 0
unpacked_dir = '%s-%s' % (srcpkg, new_ver.split('-')[0])
if 'DEBFULLNAME' in os.environ:
uploader_name = os.environ['DEBFULLNAME']
else:
uploader_name = pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0]
uploader_email = os.environ['DEBEMAIL']
genchanges_cmd = ['dpkg-genchanges', '-q', '-S', orig_arg]
if subprocess.call(['dpkg-parsechangelog', '-v%s' % cur_ver],
cwd=unpacked_dir) == 0:
genchanges_cmd.append('-v%s' % cur_ver)
genchanges_cmd.append('-e%s <%s>' % (uploader_name, uploader_email))
genchanges = subprocess.Popen(genchanges_cmd, stdout=subprocess.PIPE,
cwd=unpacked_dir)
changes_text = genchanges.communicate()[0]
changes_filename = '%s_%s_source.changes' % (srcpkg, new_ver)
changes_file = open(changes_filename, 'w')
print >>changes_file, 'Origin: %s' % urlparse.urlparse(dscurl).netloc
for line in changes_text.splitlines():
if line.startswith('Distribution:'):
line = 'Distribution: %s' % release
print >>changes_file, line
changes_file.close()
shutil.rmtree(srcpkg + '-' + new_ver.split('-')[0], True)
# strip any previous .dsc signature
dsc_file = open(dscname)
dsc_lines = [line.rstrip('\n') for line in dsc_file.readlines()]
dsc_file.close()
if dsc_lines[0] == '-----BEGIN PGP SIGNED MESSAGE-----':
while dsc_lines and dsc_lines[0] != '':
dsc_lines.pop(0)
dsc_lines.pop(0)
try:
blank = dsc_lines.index('')
dsc_lines = dsc_lines[:blank]
except ValueError:
pass
dsc_file = open(dscname, 'w')
for line in dsc_lines:
print >>dsc_file, line
dsc_file.close()
assert subprocess.call(['debsign', changes_filename]) == 0
dput_cf = '%s_%s_source.dput' % (srcpkg, new_ver)
dput_cf_file = open(dput_cf, 'w')
print >>dput_cf_file, '''\
[ppa-%s]
fqdn = ppa.launchpad.net
method = ftp
incoming = ~%s/ubuntu/
login = anonymous
allow_unsigned_uploads = 0''' % (ppa, ppa)
dput_cf_file.close()
assert subprocess.call(['dput', '-c', dput_cf, 'ppa-%s' % ppa, changes_filename]) == 0
os.unlink(dput_cf)