From 589b39c0708aecfaa1677dbc8fbc6fa8b73cd642 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Sun, 11 Jan 2009 22:20:22 +0100 Subject: [PATCH] (closes #182) Add category support to Schedulers and Changes. Signed-off-by: Dustin J. Mitchell --- buildbot/changes/changes.py | 9 +++++---- buildbot/changes/pb.py | 1 + buildbot/clients/sendchange.py | 4 ++-- buildbot/scheduler.py | 9 +++++++-- buildbot/scripts/runner.py | 4 +++- buildbot/test/test_changes.py | 17 ++++++++++++++++- buildbot/test/test_scheduler.py | 35 +++++++++++++++++++++++++++++++++++ docs/buildbot.texinfo | 9 +++++++++ 8 files changed, 78 insertions(+), 10 deletions(-) diff --git a/buildbot/changes/changes.py b/buildbot/changes/changes.py index 4c2ae3b..f58b8e0 100644 --- a/buildbot/changes/changes.py +++ b/buildbot/changes/changes.py @@ -54,7 +54,7 @@ class Change: revision = None # used to create a source-stamp def __init__(self, who, files, comments, isdir=0, links=[], - revision=None, when=None, branch=None): + revision=None, when=None, branch=None, category=None): self.who = who self.comments = comments self.isdir = isdir @@ -64,6 +64,7 @@ class Change: when = util.now() self.when = when self.branch = branch + self.category = category # keep a sorted list of the files, for easier display self.files = files[:] @@ -212,9 +213,9 @@ class ChangeMaster(service.MultiService): """Deliver a file change event. The event should be a Change object. This method will timestamp the object as it is received.""" log.msg("adding change, who %s, %d files, rev=%s, branch=%s, " - "comments %s" % (change.who, len(change.files), - change.revision, change.branch, - change.comments)) + "comments %s, category %s" % (change.who, len(change.files), + change.revision, change.branch, + change.comments, change.category)) change.number = self.nextNumber self.nextNumber += 1 self.changes.append(change) diff --git a/buildbot/changes/pb.py b/buildbot/changes/pb.py index 8a6dd8e..91a1a22 100644 --- a/buildbot/changes/pb.py +++ b/buildbot/changes/pb.py @@ -34,6 +34,7 @@ class ChangePerspective(NewCredPerspective): changedict['comments'], branch=changedict.get('branch'), revision=changedict.get('revision'), + category=changedict.get('category'), ) self.changemaster.addChange(change) diff --git a/buildbot/clients/sendchange.py b/buildbot/clients/sendchange.py index 6a01f4e..0ea4ba6 100644 --- a/buildbot/clients/sendchange.py +++ b/buildbot/clients/sendchange.py @@ -10,11 +10,11 @@ class Sender: self.port = int(self.port) self.num_changes = 0 - def send(self, branch, revision, comments, files, user=None): + def send(self, branch, revision, comments, files, user=None, category=None): if user is None: user = self.user change = {'who': user, 'files': files, 'comments': comments, - 'branch': branch, 'revision': revision} + 'branch': branch, 'revision': revision, 'category': category} self.num_changes += 1 f = pb.PBClientFactory() diff --git a/buildbot/scheduler.py b/buildbot/scheduler.py index f59655e..4341617 100644 --- a/buildbot/scheduler.py +++ b/buildbot/scheduler.py @@ -88,10 +88,10 @@ class Scheduler(BaseUpstreamScheduler): fileIsImportant = None compare_attrs = ('name', 'treeStableTimer', 'builderNames', 'branch', - 'fileIsImportant', 'properties') + 'fileIsImportant', 'properties', 'categories') def __init__(self, name, branch, treeStableTimer, builderNames, - fileIsImportant=None, properties={}): + fileIsImportant=None, properties={}, categories=None): """ @param name: the name of this Scheduler @param branch: The branch name that the Scheduler should pay @@ -116,6 +116,7 @@ class Scheduler(BaseUpstreamScheduler): @param properties: properties to apply to all builds started from this scheduler + @param categories: A list of categories of changes to accept """ BaseUpstreamScheduler.__init__(self, name, properties) @@ -136,6 +137,7 @@ class Scheduler(BaseUpstreamScheduler): self.unimportantChanges = [] self.nextBuildTime = None self.timer = None + self.categories = categories def listBuilderNames(self): return self.builderNames @@ -149,6 +151,9 @@ class Scheduler(BaseUpstreamScheduler): if change.branch != self.branch: log.msg("%s ignoring off-branch %s" % (self, change)) return + if self.categories is not None and change.category not in self.categories: + log.msg("%s ignoring non-matching categories %s" % (self, change)) + return if not self.fileIsImportant: self.addImportantChange(change) elif self.fileIsImportant(change): diff --git a/buildbot/scripts/runner.py b/buildbot/scripts/runner.py index 1f7da86..25d2586 100644 --- a/buildbot/scripts/runner.py +++ b/buildbot/scripts/runner.py @@ -717,6 +717,7 @@ class SendChangeOptions(usage.Options): "Location of the buildmaster's PBListener (host:port)"), ("username", "u", None, "Username performing the commit"), ("branch", "b", None, "Branch specifier"), + ("category", "c", None, "Category of repository"), ("revision", "r", None, "Revision specifier (string)"), ("revision_number", "n", None, "Revision specifier (integer)"), ("revision_file", None, None, "Filename containing revision spec"), @@ -739,6 +740,7 @@ def sendchange(config, runReactor=False): user = config.get('username', opts.get('username')) master = config.get('master', opts.get('master')) branch = config.get('branch', opts.get('branch')) + category = config.get('category', opts.get('category')) revision = config.get('revision') # SVN and P4 use numeric revisions if config.get("revision_number"): @@ -762,7 +764,7 @@ def sendchange(config, runReactor=False): assert master, "you must provide the master location" s = Sender(master, user) - d = s.send(branch, revision, comments, files) + d = s.send(branch, revision, comments, files, category=category) if runReactor: d.addCallbacks(s.printSuccess, s.printFailure) d.addBoth(s.stop) diff --git a/buildbot/test/test_changes.py b/buildbot/test/test_changes.py index 6d26b09..faebe7b 100644 --- a/buildbot/test/test_changes.py +++ b/buildbot/test/test_changes.py @@ -19,6 +19,10 @@ d3 = {'files': ["Project/baz.c", "OtherProject/bloo.c"], d4 = {'files': ["trunk/baz.c", "branches/foobranch/foo.c", "trunk/bar.c"], 'who': "alice", 'comments': "mixed changes"} +d5 = {'files': ["Project/foo.c"], + 'who': "trillian", + 'comments': "Some changes in Project", + 'category': "categoryA"} class TestChangePerspective(unittest.TestCase): @@ -93,7 +97,12 @@ class TestChangePerspective(unittest.TestCase): self.failUnlessEqual(set(c1.files), set(["foo.c"])) self.failUnlessEqual(c1.comments, "mixed changes") - + def testCategory(self): + p = pb.ChangePerspective(self, None) + p.perspective_addChange(d5) + self.failUnlessEqual(len(self.changes), 1) + c1 = self.changes[0] + self.failUnlessEqual(c1.category, "categoryA") config_empty = """ BuildmasterConfig = c = {} @@ -144,6 +153,7 @@ class Sender(unittest.TestCase): self.options = {'username': "alice", 'master': "localhost:%d" % port, 'files': ["foo.c"], + 'category': "categoryA", } d = runner.sendchange(self.options) @@ -158,6 +168,7 @@ class Sender(unittest.TestCase): self.failUnlessEqual(c.files, ["foo.c"]) self.failUnlessEqual(c.comments, "") self.failUnlessEqual(c.revision, None) + self.failUnlessEqual(c.category, "categoryA") self.options['revision'] = "r123" self.options['comments'] = "test change" @@ -173,6 +184,7 @@ class Sender(unittest.TestCase): self.failUnlessEqual(c.files, ["foo.c"]) self.failUnlessEqual(c.comments, "test change") self.failUnlessEqual(c.revision, "r123") + self.failUnlessEqual(c.category, "categoryA") # test options['logfile'] by creating a temporary file logfile = self.mktemp() @@ -193,6 +205,7 @@ class Sender(unittest.TestCase): self.failUnlessEqual(c.files, ["foo.c"]) self.failUnlessEqual(c.comments, "longer test change") self.failUnlessEqual(c.revision, "r123") + self.failUnlessEqual(c.category, "categoryA") # make sure that numeric revisions work too self.options['logfile'] = None @@ -210,6 +223,7 @@ class Sender(unittest.TestCase): self.failUnlessEqual(c.files, ["foo.c"]) self.failUnlessEqual(c.comments, "") self.failUnlessEqual(c.revision, 42) + self.failUnlessEqual(c.category, "categoryA") # verify --branch too self.options['branch'] = "branches/test" @@ -226,3 +240,4 @@ class Sender(unittest.TestCase): self.failUnlessEqual(c.comments, "") self.failUnlessEqual(c.revision, 42) self.failUnlessEqual(c.branch, "branches/test") + self.failUnlessEqual(c.category, "categoryA") diff --git a/buildbot/test/test_scheduler.py b/buildbot/test/test_scheduler.py index 5b93517..667e349 100644 --- a/buildbot/test/test_scheduler.py +++ b/buildbot/test/test_scheduler.py @@ -311,3 +311,38 @@ class Scheduling(unittest.TestCase): self.failUnlessEqual(s.getBuildSets(), [bs1.status]) bs1.status.notifyFinishedWatchers() self.failUnlessEqual(s.getBuildSets(), []) + + def testCategory(self): + s1 = scheduler.Scheduler("b1", "branch1", 2, ["a","b"], categories=["categoryA", "both"]) + self.addScheduler(s1) + s2 = scheduler.Scheduler("b2", "branch1", 2, ["a","b"], categories=["categoryB", "both"]) + self.addScheduler(s2) + + c0 = Change("carol", ["important"], "branch1", branch="branch1", category="categoryA") + s1.addChange(c0) + s2.addChange(c0) + + c1 = Change("carol", ["important"], "branch1", branch="branch1", category="categoryB") + s1.addChange(c1) + s2.addChange(c1) + + c2 = Change("carol", ["important"], "branch1", branch="branch1") + s1.addChange(c2) + s2.addChange(c2) + + c3 = Change("carol", ["important"], "branch1", branch="branch1", category="both") + s1.addChange(c3) + s2.addChange(c3) + + self.failUnlessEqual(s1.importantChanges, [c0, c3]) + self.failUnlessEqual(s2.importantChanges, [c1, c3]) + + s = scheduler.Scheduler("b3", "branch1", 2, ["a","b"]) + self.addScheduler(s) + + c0 = Change("carol", ["important"], "branch1", branch="branch1", category="categoryA") + s.addChange(c0) + c1 = Change("carol", ["important"], "branch1", branch="branch1", category="categoryB") + s.addChange(c1) + + self.failUnlessEqual(s.importantChanges, [c0, c1]) diff --git a/docs/buildbot.texinfo b/docs/buildbot.texinfo index cfb5cfd..d9d52ec 100644 --- a/docs/buildbot.texinfo +++ b/docs/buildbot.texinfo @@ -1671,6 +1671,10 @@ may be useful if you have a trunk and a few release branches which should be tracked, but when you don't want to have the Buildbot pay attention to several dozen private user branches. +When the setup has multiple sources of Changes the @code{category} +can be used for @code{Scheduler} objects to filter out a subset +of the Changes. + Some Schedulers may trigger builds for other reasons, other than recent Changes. For example, a Scheduler subclass could connect to a remote buildmaster and watch for builds of a library to succeed before @@ -8349,6 +8353,11 @@ This provides the (string) branch specifier. If omitted, it defaults to None, indicating the ``default branch''. All files included in this Change must be on the same branch. +@item --category +This provides the (string) category specifier. If omitted, it defaults +to None, indicating ``no category''. The category property is used +by Schedulers to filter what changes they listen to. + @item --revision_number This provides a (numeric) revision number for the change, used for VC systems that use numeric transaction numbers (like Subversion). -- 2.11.4.GIT