#!/usr/bin/python import contextlib import glob import os import shutil import subprocess import sys import tempfile import re import argparse # Use the util functions from cloudinit possible_topdir = os.path.normpath(os.path.join(os.path.abspath( sys.argv[0]), os.pardir, os.pardir)) if os.path.exists(os.path.join(possible_topdir, "cloudinit", "__init__.py")): sys.path.insert(0, possible_topdir) from cloudinit import templater from cloudinit import util from datetime import datetime # Mapping of expected packages to there full name... PKG_MP = { 'boto': 'python-boto', 'tempita': 'python-tempita', 'prettytable': 'python-prettytable', 'oauth': 'python-oauth', 'configobj': 'python-configobj', 'yaml': 'PyYAML', 'argparse': 'python-argparse' } def get_log_header(version): # Try to find the version in the tags output cmd = ['bzr', 'tags'] (stdout, _stderr) = util.subp(cmd) a_rev = None for t in stdout.splitlines(): ver, rev = t.split(None) if ver == version: a_rev = rev break if not a_rev: return format_change_line(datetime.now(), '??', version) # Extract who made that tag as the header cmd = ['bzr', 'log', '-r%s' % (a_rev), '--timezone=utc'] (stdout, _stderr) = util.subp(cmd) kvs = { 'comment': version, } for line in stdout.splitlines(): if line.startswith('committer:'): kvs['who'] = line[len('committer:'):].strip() if line.startswith('timestamp:'): ts = line[len('timestamp:'):] ts = ts.strip() # http://bugs.python.org/issue6641 ts = ts.replace("+0000", '').strip() ds = datetime.strptime(ts, '%a %Y-%m-%d %H:%M:%S') kvs['ds'] = ds return format_change_line(**kvs) def format_change_line(ds, who, comment=None): d = ds.strftime("%a %b %d %Y") d += " - %s" % (who) if comment: d += " - %s" % (comment) return "* %s" % (d) def generate_spec_contents(args, tmpl_fn): # Figure out the version and revno cmd = [sys.executable, util.abs_join(os.pardir, 'tools', 'read-version')] (stdout, _stderr) = util.subp(cmd) version = stdout.strip() cmd = ['bzr', 'revno'] (stdout, _stderr) = util.subp(cmd) revno = stdout.strip() # Tmpl params subs = {} subs['version'] = version subs['revno'] = revno subs['release'] = revno subs['archive_name'] = '%{name}-%{version}-' + revno + '.tar.gz' subs['bd_requires'] = ['python-devel', 'python-setuptools'] cmd = [sys.executable, util.abs_join(os.pardir, 'tools', 'read-dependencies')] (stdout, _stderr) = util.subp(cmd) # Map to known packages pkgs = [p.lower().strip() for p in stdout.splitlines()] # Map to known packages requires = [] for p in pkgs: tgt_pkg = None for name in PKG_MP.keys(): if p.find(name) != -1: tgt_pkg = PKG_MP.get(name) break if not tgt_pkg: raise RuntimeError(("Do not know how to translate %s to " " a known package") % (p)) else: requires.append(tgt_pkg) subs['requires'] = requires # Format a nice changelog (as best as we can) changelog = util.load_file(util.abs_join(os.pardir, 'ChangeLog')) changelog_lines = [] for line in changelog.splitlines(): if not line.strip(): continue if re.match(r"^\s*[\d][.][\d][.][\d]:\s*", line): line = line.strip(":") header = get_log_header(line) changelog_lines.append(header) else: changelog_lines.append(line) subs['changelog'] = "\n".join(changelog_lines) # See: http://www.zarb.org/~jasonc/macros.php # Pickup any special files docs = [ 'TODO', 'LICENSE', 'ChangeLog', 'Requires', '%{_defaultdocdir}/cloud-init/*', ] subs['docs'] = docs configs = [ 'cloud/cloud.cfg', 'cloud/cloud.cfg.d/*.cfg', 'cloud/cloud.cfg.d/README', 'cloud/templates/*', ] subs['configs'] = configs other_files = [ '%{_bindir}/*', '/usr/lib/cloud-init/*', ] # Since setup.py installs them # all, we need to selectively # knock off the wrong ones and # ensure the right one is kept post_remove_keep = { 'initd': '/etc/init.d/', 'systemd': '/etc/systemd/', 'upstart': '/etc/init/', } post_remove = [] for (k, v) in post_remove_keep.iteritems(): if k != args.boot: post_remove.append(v) else: take = v + "*" other_files.append(take) subs['post_remove'] = post_remove subs['files'] = other_files return templater.render_from_file(tmpl_fn, params=subs) def archive_code(): (stdout, _stderr) = tiny_p([sys.executable, join(os.getcwd(), 'make-tarball')]) (revno, version, bname, arc_fn) = stdout.split(None) return (revno, version, arc_fn) def main(): parser = argparse.ArgumentParser() parser.add_argument("-b", "--boot", dest="boot", help="select boot type (default: %(default)s)", metavar="TYPE", default='initd', choices=['upstart', 'initd', 'systemd']) parser.add_argument("-v", "--verbose", dest="verbose", help=("run verbosely" " (default: %(default)s)"), default=False, action='store_true') args = parser.parse_args() capture = True if args.verbose: capture = False # Clean out the root dir and make sure the dirs we want are in place root_dir = os.path.expanduser("~/rpmbuild") if os.path.isdir(root_dir): shutil.rmtree(root_dir) arc_dir = util.abs_join(root_dir, 'SOURCES') util.ensure_dirs([root_dir, arc_dir]) # Archive the code cmd = [sys.executable, util.abs_join(os.getcwd(), 'make-tarball')] (stdout, _stderr) = util.subp(cmd) archive_fn = stdout.strip() real_archive_fn = os.path.join(arc_dir, os.path.basename(archive_fn)) shutil.move(archive_fn, real_archive_fn) # Form the spec file to be used tmpl_fn = util.abs_join(os.getcwd(), 'redhat', 'cloud-init.spec') contents = generate_spec_contents(args, tmpl_fn) spec_fn = os.path.join(root_dir, 'cloud-init.spec') util.write_file(spec_fn, contents) # Now build it! cmd = ['rpmbuild', '-ba', spec_fn] util.subp(cmd, capture=capture) # Copy the items built to our local dir globs = [] globs.extend(glob.glob("%s/*.rpm" % (os.path.join(root_dir, 'RPMS', 'noarch')))) globs.extend(glob.glob("%s/*.rpm" % (os.path.join(root_dir, 'RPMS')))) globs.extend(glob.glob("%s/*.rpm" % (os.path.join(root_dir, 'SRPMS')))) for rpm_fn in globs: tgt_fn = util.abs_join(os.getcwd(), os.path.basename(rpm_fn)) shutil.move(rpm_fn, tgt_fn) print(tgt_fn) return 0 if __name__ == '__main__': sys.exit(main())