#!/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)