
It seems like its possible that boto 2.5.2 and below have the lazy loading metadata dictionary so as a precaution we will always take the hit of unlazying the metadata dictionary by traversing it which in the non-lazy dictionary case has no effect (its marginal). This also removes the need to check the boto version and the dependency on setup tools just for this case.
252 lines
8.2 KiB
Python
Executable File
252 lines
8.2 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import argparse
|
|
import contextlib
|
|
import glob
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import re
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
def find_root():
|
|
# expected path is in <top_dir>/packages/
|
|
top_dir = os.environ.get("CLOUD_INIT_TOP_D", None)
|
|
if top_dir is None:
|
|
top_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
|
|
if os.path.isfile(os.path.join(top_dir, 'setup.py')):
|
|
return os.path.abspath(top_dir)
|
|
raise OSError(("Unable to determine where your cloud-init topdir is."
|
|
" set CLOUD_INIT_TOP_D?"))
|
|
|
|
|
|
# Use the util functions from cloudinit
|
|
sys.path.insert(0, find_root())
|
|
|
|
from cloudinit import templater
|
|
from cloudinit import util
|
|
|
|
# Mapping of expected packages to there full name...
|
|
# this is a translation of the 'requires'
|
|
# file pypi package name to a redhat/fedora package name.
|
|
PKG_MP = {
|
|
'argparse': 'python-argparse',
|
|
'boto': 'python-boto',
|
|
'cheetah': 'python-cheetah',
|
|
'configobj': 'python-configobj',
|
|
'oauth': 'python-oauth',
|
|
'prettytable': 'python-prettytable',
|
|
'pyyaml': 'PyYAML',
|
|
}
|
|
|
|
# Subdirectories of the ~/rpmbuild dir
|
|
RPM_BUILD_SUBDIRS = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS']
|
|
|
|
|
|
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 None
|
|
|
|
# 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):
|
|
# Rpmbuild seems to be pretty strict about the date format
|
|
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, top_dir, arc_fn):
|
|
|
|
# Figure out the version and revno
|
|
cmd = [util.abs_join(find_root(), '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'] = "bzr%s" % (revno)
|
|
if args.sub_release is not None:
|
|
subs['subrelease'] = "." + str(args.sub_release)
|
|
else:
|
|
subs['subrelease'] = ''
|
|
subs['archive_name'] = arc_fn
|
|
|
|
cmd = [util.abs_join(find_root(), 'tools', 'read-dependencies')]
|
|
(stdout, _stderr) = util.subp(cmd)
|
|
pkgs = [p.lower().strip() for p in stdout.splitlines()]
|
|
|
|
# Map to known packages
|
|
requires = []
|
|
for p in pkgs:
|
|
tgt_pkg = PKG_MP.get(p)
|
|
if not tgt_pkg:
|
|
raise RuntimeError(("Do not know how to translate pypi dependency"
|
|
" %r 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(find_root(), 'ChangeLog'))
|
|
changelog_lines = []
|
|
missing_versions = 0
|
|
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)
|
|
if not header:
|
|
missing_versions += 1
|
|
if missing_versions == 1:
|
|
# Must be using a new 'dev'/'trunk' release
|
|
changelog_lines.append(format_change_line(datetime.now(), '??'))
|
|
else:
|
|
sys.stderr.write(("Changelog version line %s "
|
|
"does not have a corresponding tag!\n") % (line))
|
|
else:
|
|
changelog_lines.append(header)
|
|
else:
|
|
changelog_lines.append(line)
|
|
subs['changelog'] = "\n".join(changelog_lines)
|
|
|
|
if args.boot == 'sysvinit':
|
|
subs['sysvinit'] = True
|
|
else:
|
|
subs['sysvinit'] = False
|
|
|
|
if args.boot == 'systemd':
|
|
subs['systemd'] = True
|
|
else:
|
|
subs['systemd'] = False
|
|
|
|
subs['defines'] = ["_topdir %s" % (top_dir)]
|
|
subs['init_sys'] = args.boot
|
|
subs['patches'] = [os.path.basename(p) for p in args.patches]
|
|
return templater.render_from_file(tmpl_fn, params=subs)
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-b", "--boot", dest="boot",
|
|
help="select boot type (default: %(default)s)",
|
|
metavar="TYPE", default='sysvinit',
|
|
choices=('sysvinit', 'systemd'))
|
|
parser.add_argument("-v", "--verbose", dest="verbose",
|
|
help=("run verbosely"
|
|
" (default: %(default)s)"),
|
|
default=False,
|
|
action='store_true')
|
|
parser.add_argument('-s', "--sub-release", dest="sub_release",
|
|
metavar="RELEASE",
|
|
help=("a 'internal' release number to concat"
|
|
" with the bzr version number to form"
|
|
" the final version number"),
|
|
type=int,
|
|
default=None)
|
|
parser.add_argument("-p", "--patch", dest="patches",
|
|
help=("include the following patch when building"),
|
|
default=[],
|
|
action='append')
|
|
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')
|
|
build_dirs = [root_dir, arc_dir]
|
|
for dname in RPM_BUILD_SUBDIRS:
|
|
build_dirs.append(util.abs_join(root_dir, dname))
|
|
build_dirs.sort()
|
|
util.ensure_dirs(build_dirs)
|
|
|
|
# Archive the code
|
|
cmd = [util.abs_join(find_root(), 'tools', '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)
|
|
print("Archived the code in %r" % (real_archive_fn))
|
|
|
|
# Form the spec file to be used
|
|
tmpl_fn = util.abs_join(find_root(), 'packages',
|
|
'redhat', 'cloud-init.spec.in')
|
|
contents = generate_spec_contents(args, tmpl_fn, root_dir,
|
|
os.path.basename(archive_fn))
|
|
spec_fn = util.abs_join(root_dir, 'cloud-init.spec')
|
|
util.write_file(spec_fn, contents)
|
|
print("Created spec file at %r" % (spec_fn))
|
|
for p in args.patches:
|
|
util.copy(p, util.abs_join(arc_dir, os.path.basename(p)))
|
|
|
|
# Now build it!
|
|
print("Running 'rpmbuild' in %r" % (root_dir))
|
|
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" %
|
|
(util.abs_join(root_dir, 'RPMS', 'noarch'))))
|
|
globs.extend(glob.glob("%s/*.rpm" %
|
|
(util.abs_join(root_dir, 'RPMS'))))
|
|
globs.extend(glob.glob("%s/*.rpm" %
|
|
(util.abs_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("Wrote out redhat package %r" % (tgt_fn))
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|