From 39f3d8a9428e031c4bdb0cc9a26f2fb1df9a7520 Mon Sep 17 00:00:00 2001 From: Daniel Narvaez Date: Sun, 16 Dec 2012 16:53:36 +0000 Subject: Add a distribute command It builds and uploads releases. --- diff --git a/Makefile.commands b/Makefile.commands index 78805a8..be82830 100644 --- a/Makefile.commands +++ b/Makefile.commands @@ -27,6 +27,9 @@ bug-report: clean: @$(COMMANDS_DIR)/clean +distribute: + @$(COMMANDS_DIR)/distribute + build-%: @$(COMMANDS_DIR)/build $* diff --git a/commands/distribute b/commands/distribute new file mode 100755 index 0000000..1da8b0b --- /dev/null +++ b/commands/distribute @@ -0,0 +1,12 @@ +#!/usr/bin/python -u + +import sys + +import common + +from devbot import build + +common.setup() + +if not build.distribute(): + sys.exit(1) diff --git a/config/modules/sugar.json b/config/modules/sugar.json index 2c119e5..20a54c3 100644 --- a/config/modules/sugar.json +++ b/config/modules/sugar.json @@ -44,6 +44,7 @@ }, { "name": "sugar-runner", - "repo": "git://git.sugarlabs.org/sugar-runner/sugar-runner.git" + "repo": "git://git.sugarlabs.org/sugar-runner/sugar-runner.git", + "distribute": true } ] diff --git a/devbot/build.py b/devbot/build.py index ee15192..9823310 100644 --- a/devbot/build.py +++ b/devbot/build.py @@ -1,14 +1,20 @@ import fnmatch +import re import os import multiprocessing import shutil import subprocess +from distutils.sysconfig import parse_makefile from devbot import command from devbot import config from devbot import environ from devbot import state from devbot import utils +from devbot import release + +_builders = {} +_distributors = {} def build_one(module_name): environ.setup() @@ -72,6 +78,16 @@ def build(): return True +def distribute(): + environ.setup() + + for module in config.load_modules(): + if module.distribute: + if not _distribute_module(module): + break + + return True + def clean(): _rmtree(config.install_dir) _rmtree(config.prefix_dir) @@ -134,10 +150,53 @@ def _build_autotools(module, log): _unlink_libtool_files() +_builders["autotools"] = _build_autotools + def _build_activity(module, log): setup = os.path.join(module.get_source_dir(), "setup.py") command.run([setup, "install", "--prefix", config.prefix_dir], log) +_builders["activity"] = _build_activity + +def _distribute_autotools(module): + makefile = parse_makefile("Makefile") + filename = makefile["DIST_ARCHIVES"] + version = makefile["VERSION"] + + git_module = module.get_git_module() + + version_revision = None + description = git_module.describe() + if description != "v%s" % version: + match = re.match(r"(v[\d\.]+)", description) + if match is None: + print "No version tag was found" + return False + else: + version_revision = match.groups()[0] + + if version_revision is not None: + git_module.checkout(version_revision) + + command.run(["make", "distcheck"]) + + result = False + + if not release.exists(module, filename): + path = os.path.join(os.getcwd(), filename) + if release.upload(module, path): + annotation = git_module.get_annotation("v%s" % version) + result = release.announce(module, filename, version, annotation) + else: + print "Release already uploaded" + + if version_revision is not None: + git_module.checkout() + + return result + +_distributors["autotools"] = _distribute_autotools + def _build_module(module, log=None): print "\n=== Building %s ===\n" % module.name @@ -159,14 +218,11 @@ def _build_module(module, log=None): os.chdir(source_dir) try: - if os.path.exists(os.path.join(source_dir, "setup.py")): - _build_activity(module, log) - elif os.path.exists(os.path.join(source_dir, "autogen.sh")): - _build_autotools(module, log) - else: - print "The source directory has unexpected content, please " \ - "delete it and pull\nthe source again." + build_system = module.get_build_system() + if build_system is None: return False + + _builders[build_system](module, log) except subprocess.CalledProcessError: return False @@ -174,6 +230,29 @@ def _build_module(module, log=None): return True +def _distribute_module(module, log=None): + print "\n=== Distribute %s ===\n" % module.name + + build_dir = module.get_build_dir() + + if not os.path.exists(build_dir): + print "Build directory does not exist. Please build before " \ + "distributing." + return False + + os.chdir(build_dir) + + try: + build_system = module.get_build_system() + if build_system is None: + return False + + _distributors[build_system](module) + except subprocess.CalledProcessError: + return False + + return True + def _rmtree(dir): print "Deleting %s" % dir shutil.rmtree(dir, ignore_errors=True) diff --git a/devbot/config.py b/devbot/config.py index 2f9999c..93c82f7 100644 --- a/devbot/config.py +++ b/devbot/config.py @@ -38,6 +38,7 @@ class Module: self.options = info.get("options", []) self.options_evaluated = info.get("options_evaluated", []) self.has_tests = info.get("has_tests", False) + self.distribute = info.get("distribute", False) if get_pref("BUILD_IN_SOURCE"): self.out_of_source = False @@ -50,7 +51,7 @@ class Module: def get_build_dir(self): return os.path.join(get_build_dir(), self.name) - def get_commit_id(self): + def get_commit_id(self, tag="HEAD"): if not os.path.exists(self.get_source_dir()): return None @@ -61,6 +62,17 @@ class Module: remote=self.repo, branch=self.branch, tag=self.tag, retry=10) + def get_build_system(self): + source_dir = self.get_source_dir() + if os.path.exists(os.path.join(source_dir, "setup.py")): + return "activity" + elif os.path.exists(os.path.join(source_dir, "autogen.sh")): + return "autotools" + else: + print "The source directory has unexpected content, please " \ + "delete it and pull\nthe source again." + return None + def _ensure_dir(dir): if not os.path.exists(dir): os.mkdir(dir) diff --git a/devbot/git.py b/devbot/git.py index b9008af..1e361f0 100644 --- a/devbot/git.py +++ b/devbot/git.py @@ -1,7 +1,20 @@ import os +import subprocess from devbot import command +def _chdir(func): + def wrapped(*args, **kwargs): + orig_cwd = os.getcwd() + + os.chdir(args[0].local) + result = func(*args, **kwargs) + os.chdir(orig_cwd) + + return result + + return wrapped + class Module: def __init__(self, path=None, name=None, remote=None, branch="master", tag=None, retry=10): @@ -45,6 +58,40 @@ class Module: command.run(["git", "merge", "--ff-only", "origin/%s" % self._branch]) + @_chdir + def checkout(self, revision=None): + if revision is None: + revision = self.tag + if revision is None: + revision = self._branch + + command.run(["git", "checkout", revision]) + + @_chdir + def describe(self): + return subprocess.check_output(["git", "describe"]).strip() + + @_chdir + def get_annotation(self, tag): + # FIXME this is fragile, there must be a better way + + show = subprocess.check_output(["git", "show", tag]) + + annotation = [] + for line in show.split("\n"): + ignore = False + for start in ["tag ", "Tagger: ", "Date: "]: + if line.startswith(start): + ignore = True + + if line.startswith("commit "): + break + + if not ignore: + annotation.append(line) + + return "\n".join(annotation) + def clean(self): try: os.chdir(self.local) diff --git a/devbot/release.py b/devbot/release.py new file mode 100644 index 0000000..278cbf5 --- /dev/null +++ b/devbot/release.py @@ -0,0 +1,47 @@ +import os +import subprocess +import tempfile + +upload_host = "shell.sugarlabs.org" +#upload_root = "/upload/sources/sucrose/glucose" +upload_root = "~/" +download_uri = "http://download.sugarlabs.org/sources/sucrose/glucose" +#announce_to = "sugar-devel@lists.sugarlabs.org" +announce_to = "dwnarvaez@gmail.com" + +def exists(module, filename): + release_path = os.path.join(upload_root, module.name, filename) + result = subprocess.call(["ssh", upload_host, "test", "-f", release_path]) + return result == 0 + +def upload(module, path): + upload_path = os.path.join(upload_root, module.name) + upload_dest = "%s:%s" % (upload_host, upload_path) + return subprocess.call(["scp", path, upload_dest]) == 0 + +def announce(module, filename, version, annotation): + fd, announce_path = tempfile.mkstemp("announce-") + + with os.fdopen(fd, "w") as f: + f.write("From: %s\n" % _get_email()) + f.write("To: %s\n" % announce_to) + f.write("Subject: [RELEASE] %s-%s\n" % (module.name, version)) + + f.write("\n%s\n" % annotation) + f.write("== Source ==\n\n") + f.write("%s/%s/%s" % (download_uri, module.name, filename)) + + result = False + + upload_dest = "%s:~" % upload_host + if subprocess.call(["scp", announce_path, upload_dest]) == 0: + if subprocess.call(["ssh", upload_host, "sendmail", "-t", + "<", os.path.basename(announce_path)]): + result = True + + os.unlink(announce_path) + + return result + +def _get_email(): + return subprocess.check_output(['git', 'config', 'user.email']).strip() -- cgit v0.9.1