#!/usr/bin/python # Copyright (c) 2009 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 2, or (at your option) any # later version. # # lp-project-upload 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. # Authors: # Martin Pitt , based on # http://blog.launchpad.net/api/recipe-for-uploading-files-via-the-api '''Upload a release tarball to a Launchpad project.''' import datetime import os import subprocess import sys import tempfile from ubuntutools.lp.libsupport import get_launchpad from launchpadlib.errors import HTTPError def create_release(project, version): '''Create new release and milestone for LP project.''' print 'Release %s could not be found for project. Create it? (Y/n)' % \ version answer = sys.stdin.readline().strip() if answer.startswith('n'): sys.exit(0) n_series = len(project.series) if n_series == 1: series = project.series[0] elif n_series > 1: msg = 'More than one series exist. Which one would you like to ' \ 'upload to? Possible series are (listed as index, name):' print msg for idx, serie in enumerate(project.series): print '\t%i - %s' % (idx, serie.name) print 'Enter series index: ' answer = sys.stdin.readline().strip() try: series = project.series[int(answer)] except (ValueError, IndexError): print >> sys.stderr, 'The series index is invalid (%s).' % answer sys.exit(3) else: print "Using series named '%s'" % series.name else: print >> sys.stderr, ('Does not support creating releases if no ' 'series exists.') sys.exit(3) release_date = datetime.date.today().strftime('%Y-%m-%d') milestone = series.newMilestone(name=version, date_targeted=release_date) return milestone.createProductRelease(date_released=release_date) def edit_file(prefix, description): (fd, f) = tempfile.mkstemp(prefix=prefix+'.') os.write(fd, '\n\n#------\n# Please enter the %s here. ' 'Lines which start with "#" are ignored.\n' % description) os.close(fd) subprocess.call(['sensible-editor', f]) content = '' for line in open(f): if line.startswith('#'): continue content += line return content.strip() def main(): if len(sys.argv) != 4: print >> sys.stderr, '''Upload a release tarball to a Launchpad project. Usage: %s ''' % sys.argv[0] sys.exit(1) (project, version, tarball) = sys.argv[1:] try: launchpad = get_launchpad('ubuntu-dev-tools') except Exception, error: print >> sys.stderr, 'Could not connect to Launchpad:', str(error) sys.exit(2) try: # Look up the project using the Launchpad instance. proj = launchpad.projects[project] # Find the release in the project's releases collection. release = None for rel in proj.releases: if rel.version == version: release = rel break if not release: release = create_release(proj, version) # Get the file contents. file_content = open(tarball, 'r').read() # Get the signature, if available. signature = tarball + '.asc' if not os.path.exists(signature): print 'Calling GPG to create tarball signature...' cmd = ['gpg', '--armor', '--sign', '--detach-sig', tarball] if subprocess.call(cmd) != 0: print >> sys.stderr, 'gpg failed, aborting' if os.path.exists(signature): signature_content = open(signature, 'r').read() else: signature_content = None # Create a new product release file. filename = os.path.basename(tarball) release.add_file(filename=filename, description='release tarball', file_content=file_content, content_type='appplication/x-gzip', file_type='Code Release Tarball', signature_filename=signature, signature_content=signature_content) changelog = edit_file('changelog', 'changelog') if changelog: release.changelog = changelog release_notes = edit_file('releasenotes', 'release notes') if release_notes: release.release_notes = release_notes release.lp_save() except HTTPError, error: print 'An error happened in the upload:', error.content sys.exit(1) if __name__ == '__main__': main()