[cig-commits] r5895 - in cs/buildbot/trunk: buildbot buildbot/changes buildbot/process buildbot/status buildbot/steps buildbot/test contrib

leif at geodynamics.org leif at geodynamics.org
Thu Jan 25 14:53:26 PST 2007


Author: leif
Date: 2007-01-25 14:53:26 -0800 (Thu, 25 Jan 2007)
New Revision: 5895

Modified:
   cs/buildbot/trunk/buildbot/changes/changes.py
   cs/buildbot/trunk/buildbot/changes/pb.py
   cs/buildbot/trunk/buildbot/process/builder.py
   cs/buildbot/trunk/buildbot/scheduler.py
   cs/buildbot/trunk/buildbot/sourcestamp.py
   cs/buildbot/trunk/buildbot/status/builder.py
   cs/buildbot/trunk/buildbot/status/html.py
   cs/buildbot/trunk/buildbot/steps/source.py
   cs/buildbot/trunk/buildbot/test/test_scheduler.py
   cs/buildbot/trunk/contrib/svn_buildbot.py
Log:
Added an additional property to BuildBot changes: 'root'.  This is
useful for segregating multiple projects which share a single
repository.  For example, when browsing "changes/NN" on the BuildBot
web page, instead of

    Branch: mc/3D/CitcomS/trunk

it will instead be broken-out as follows:

    Root: mc/3D/CitcomS/
    Branch: trunk

(Is the trunk a branch?  Hmmm...)  The motivation here comes from the
database-style queries.  For example, if one wants to see a waterfall
display of all builders for all branches of CitcomS, or just a
specific branch, it is easier to construct the query if 'branch' is
project-relative:

    all branches:
        http://localhost:8010/?project=CitcomS

    just 'compressible':
        http://localhost:8010/?project=CitcomS&branch=compressible

The first query would be messy with the (project) root and the branch
lumped together, as they were before... one would have to do some sort
of wildcard matching:

    branch=mc/3D/CitcomS/*

Of course, I want the 'changes' column to only show changes relevant
to the current query (not yet implemented), so 'Change' objects must
have a project-relative 'branch'... and this change to 'Change'
ripples thoughout BuildBot as illustrated by this check-in.

Note: I did not bother updating the classes for the other (non-SVN)
source control systems, as it would be too cumbersome to test.


Modified: cs/buildbot/trunk/buildbot/changes/changes.py
===================================================================
--- cs/buildbot/trunk/buildbot/changes/changes.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/changes/changes.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -19,6 +19,7 @@
 html_tmpl = """
 <p>Changed by: <b>%(who)s</b><br />
 Changed at: <b>%(at)s</b><br />
+%(root)s
 %(branch)s
 %(revision)s
 <br />
@@ -59,11 +60,12 @@
     number = None
 
     links = []
+    root = None
     branch = None
     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, root=None, branch=None):
         self.who = who
         self.files = files
         self.comments = comments
@@ -73,6 +75,7 @@
         if when is None:
             when = util.now()
         self.when = when
+        self.root = root
         self.branch = branch
 
     def asText(self):
@@ -95,6 +98,9 @@
         revision = ""
         if self.revision:
             revision = "Revision: <b>%s</b><br />\n" % self.revision
+        root = ""
+        if self.root:
+            root = "Root: <b>%s</b><br />\n" % self.root
         branch = ""
         if self.branch:
             branch = "Branch: <b>%s</b><br />\n" % self.branch
@@ -103,6 +109,7 @@
                    'at'      : self.getTime(),
                    'files'   : html.UL(links) + '\n',
                    'revision': revision,
+                   'root'    : root,
                    'branch'  : branch,
                    'comments': html.PRE(self.comments) }
         return html_tmpl % kwargs
@@ -196,9 +203,10 @@
     def addChange(self, change):
         """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, "
+        log.msg("adding change, who %s, %d files, rev=%s, root=%s, branch=%s, "
                 "comments %s" % (change.who, len(change.files),
-                                 change.revision, change.branch,
+                                 change.revision,
+                                 change.root, change.branch,
                                  change.comments))
         change.number = self.nextNumber
         self.nextNumber += 1

Modified: cs/buildbot/trunk/buildbot/changes/pb.py
===================================================================
--- cs/buildbot/trunk/buildbot/changes/pb.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/changes/pb.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -32,6 +32,7 @@
             change = changes.Change(changedict['who'],
                                     pathnames,
                                     changedict['comments'],
+                                    root=changedict.get('root'),
                                     branch=changedict.get('branch'),
                                     revision=changedict.get('revision'),
                                     )

Modified: cs/buildbot/trunk/buildbot/process/builder.py
===================================================================
--- cs/buildbot/trunk/buildbot/process/builder.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/process/builder.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -660,9 +660,9 @@
     def resubmitBuild(self, bs, reason="<rebuild, no reason given>"):
         if not bs.isFinished():
             return
-        branch, revision, patch = bs.getSourceStamp()
+        root, branch, revision, patch = bs.getSourceStamp()
         changes = bs.getChanges()
-        ss = sourcestamp.SourceStamp(branch, revision, patch, changes)
+        ss = sourcestamp.SourceStamp(root, branch, revision, patch, changes)
         req = base.BuildRequest(reason, ss, self.original.name)
         self.requestBuild(req)
 

Modified: cs/buildbot/trunk/buildbot/scheduler.py
===================================================================
--- cs/buildbot/trunk/buildbot/scheduler.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/scheduler.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -76,10 +76,10 @@
     """
 
     fileIsImportant = None
-    compare_attrs = ('name', 'treeStableTimer', 'builderNames', 'branch',
+    compare_attrs = ('name', 'treeStableTimer', 'builderNames', 'root', 'branch',
                      'fileIsImportant')
     
-    def __init__(self, name, branch, treeStableTimer, builderNames,
+    def __init__(self, name, root, branch, treeStableTimer, builderNames,
                  fileIsImportant=None):
         """
         @param name: the name of this Scheduler
@@ -113,6 +113,7 @@
         for b in builderNames:
             assert isinstance(b, str), errmsg
         self.builderNames = builderNames
+        self.root = root
         self.branch = branch
         if fileIsImportant:
             assert callable(fileIsImportant)
@@ -132,6 +133,9 @@
         return []
 
     def addChange(self, change):
+        if change.root != self.root:
+            log.msg("%s ignoring off-root %s" % (self, change))
+            return
         if change.branch != self.branch:
             log.msg("%s ignoring off-branch %s" % (self, change))
             return
@@ -194,10 +198,10 @@
     schedulerFactory = Scheduler
     fileIsImportant = None
 
-    compare_attrs = ('name', 'branches', 'treeStableTimer', 'builderNames',
+    compare_attrs = ('name', 'roots', 'branches', 'treeStableTimer', 'builderNames',
                      'fileIsImportant')
 
-    def __init__(self, name, branches, treeStableTimer, builderNames,
+    def __init__(self, name, roots, branches, treeStableTimer, builderNames,
                  fileIsImportant=None):
         """
         @param name: the name of this Scheduler
@@ -230,6 +234,11 @@
         for b in builderNames:
             assert isinstance(b, str)
         self.builderNames = builderNames
+        self.roots = roots
+        if self.roots == []:
+            log.msg("AnyBranchScheduler %s: roots=[], so we will ignore "
+                    "all roots, and never trigger any builds. Please set "
+                    "roots=None to mean 'all roots'" % self)
         self.branches = branches
         if self.branches == []:
             log.msg("AnyBranchScheduler %s: branches=[], so we will ignore "
@@ -258,17 +267,22 @@
         return bts
 
     def addChange(self, change):
+        root = change.root
+        if self.roots is not None and root not in self.roots:
+            log.msg("%s ignoring off-root %s" % (self, change))
+            return
         branch = change.branch
         if self.branches is not None and branch not in self.branches:
             log.msg("%s ignoring off-branch %s" % (self, change))
             return
-        s = self.schedulers.get(branch)
+        path = root + branch
+        s = self.schedulers.get(path)
         if not s:
             if branch:
-                name = self.name + "." + branch
+                name = self.name + "." + root + branch
             else:
-                name = self.name + ".<default>"
-            s = self.schedulerFactory(name, branch,
+                name = self.name + "." + root + "<default>"
+            s = self.schedulerFactory(name, root, branch,
                                       self.treeStableTimer,
                                       self.builderNames,
                                       self.fileIsImportant)
@@ -276,7 +290,7 @@
             s.setServiceParent(self)
             # TODO: does this result in schedulers that stack up forever?
             # When I make the persistify-pass, think about this some more.
-            self.schedulers[branch] = s
+            self.schedulers[path] = s
         s.addChange(change)
 
     def submitBuildSet(self, bs):

Modified: cs/buildbot/trunk/buildbot/sourcestamp.py
===================================================================
--- cs/buildbot/trunk/buildbot/sourcestamp.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/sourcestamp.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -19,30 +19,34 @@
        from the given branch.
     """
 
-    # all four of these are publically visible attributes
+    # all five of these are publically visible attributes
+    root = None
     branch = None
     revision = None
     patch = None
     changes = []
 
-    compare_attrs = ('branch', 'revision', 'patch', 'changes')
+    compare_attrs = ('root', 'branch', 'revision', 'patch', 'changes')
 
     if implements:
         implements(interfaces.ISourceStamp)
     else:
         __implements__ = interfaces.ISourceStamp,
 
-    def __init__(self, branch=None, revision=None, patch=None,
+    def __init__(self, root=None, branch=None, revision=None, patch=None,
                  changes=None):
+        self.root = root
         self.branch = branch
         self.revision = revision
         self.patch = patch
         if changes:
             self.changes = changes
+            self.root = changes[0].root
             self.branch = changes[0].branch
 
     def canBeMergedWith(self, other):
-        if other.branch != self.branch:
+        if (other.root != self.root or
+            other.branch != self.branch):
             return False # the builds are completely unrelated
 
         if self.changes and other.changes:
@@ -77,7 +81,8 @@
         for req in others:
             assert self.canBeMergedWith(req) # should have been checked already
             changes.extend(req.changes)
-        newsource = SourceStamp(branch=self.branch,
+        newsource = SourceStamp(root=self.root,
+                                branch=self.branch,
                                 revision=self.revision,
                                 patch=self.patch,
                                 changes=changes)

Modified: cs/buildbot/trunk/buildbot/status/builder.py
===================================================================
--- cs/buildbot/trunk/buildbot/status/builder.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/status/builder.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -979,7 +979,7 @@
         return self.builder.getBuild(self.number-1)
 
     def getSourceStamp(self):
-        return (self.source.branch, self.source.revision, self.source.patch)
+        return (self.source.root, self.source.branch, self.source.revision, self.source.patch)
 
     def getReason(self):
         return self.reason

Modified: cs/buildbot/trunk/buildbot/status/html.py
===================================================================
--- cs/buildbot/trunk/buildbot/status/html.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/status/html.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -334,9 +334,11 @@
         data += "<h2>Buildslave:</h2>\n %s\n" % html.escape(b.getSlavename())
         data += "<h2>Reason:</h2>\n%s\n" % html.escape(b.getReason())
 
-        branch, revision, patch = b.getSourceStamp()
+        root, branch, revision, patch = b.getSourceStamp()
         data += "<h2>SourceStamp:</h2>\n"
         data += " <ul>\n"
+        if root:
+            data += "  <li>Root: %s</li>\n" % html.escape(root)
         if branch:
             data += "  <li>Branch: %s</li>\n" % html.escape(branch)
         if revision:

Modified: cs/buildbot/trunk/buildbot/steps/source.py
===================================================================
--- cs/buildbot/trunk/buildbot/steps/source.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/steps/source.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -159,6 +159,8 @@
 
         # what source stamp would this build like to use?
         s = self.build.getSourceStamp()
+        # if root is None, then use the Step's "default" root
+        root = s.root or self.root
         # if branch is None, then use the Step's "default" branch
         branch = s.branch or self.branch
         # if revision is None, use the latest sources (-rHEAD)
@@ -170,7 +172,7 @@
         # 'patch' is None or a tuple of (patchlevel, diff)
         patch = s.patch
 
-        self.startVC(branch, revision, patch)
+        self.startVC(root, branch, revision, patch)
 
     def commandComplete(self, cmd):
         got_revision = None
@@ -346,7 +348,8 @@
 
     name = 'svn'
 
-    def __init__(self, svnurl=None, baseURL=None, defaultBranch=None,
+    def __init__(self, svnurl=None, baseURL=None,
+                 root=None, defaultBranch=None,
                  directory=None, **kwargs):
         """
         @type  svnurl: string
@@ -378,6 +381,7 @@
 
         self.svnurl = svnurl
         self.baseURL = baseURL
+        self.root = root
         self.branch = defaultBranch
 
         Source.__init__(self, **kwargs)
@@ -392,7 +396,7 @@
         lastChange = max([int(c.revision) for c in changes])
         return lastChange
 
-    def startVC(self, branch, revision, patch):
+    def startVC(self, root, branch, revision, patch):
 
         # handle old slaves
         warnings = []
@@ -407,7 +411,7 @@
             # the last build used, so if we're using a non-default branch and
             # either 'update' or 'copy' modes, it is safer to refuse to
             # build, and tell the user they need to upgrade the buildslave.
-            if (branch != self.branch
+            if ((root != self.root or branch != self.branch)
                 and self.args['mode'] in ("update", "copy")):
                 m = ("This buildslave (%s) does not know about multiple "
                      "branches, and using mode=%s would probably build the "
@@ -447,7 +451,7 @@
             assert not branch # we need baseURL= to use branches
             self.args['svnurl'] = self.svnurl
         else:
-            self.args['svnurl'] = self.baseURL + branch
+            self.args['svnurl'] = self.baseURL + root + branch
         self.args['revision'] = revision
         self.args['patch'] = patch
 

Modified: cs/buildbot/trunk/buildbot/test/test_scheduler.py
===================================================================
--- cs/buildbot/trunk/buildbot/test/test_scheduler.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/buildbot/test/test_scheduler.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -126,7 +126,7 @@
 
 
     def testAnyBranch(self):
-        s = scheduler.AnyBranchScheduler("b1", None, 1, ["a","b"],
+        s = scheduler.AnyBranchScheduler("b1", None, None, 1, ["a","b"],
                                          fileIsImportant=self.isImportant)
         self.addScheduler(s)
 
@@ -175,7 +175,7 @@
 
     def testAnyBranch2(self):
         # like testAnyBranch but without fileIsImportant
-        s = scheduler.AnyBranchScheduler("b1", None, 2, ["a","b"])
+        s = scheduler.AnyBranchScheduler("b1", None, None, 2, ["a","b"])
         self.addScheduler(s)
         c1 = Change("alice", ["important", "not important"], "some changes",
                     branch="branch1")

Modified: cs/buildbot/trunk/contrib/svn_buildbot.py
===================================================================
--- cs/buildbot/trunk/contrib/svn_buildbot.py	2007-01-25 20:12:24 UTC (rev 5894)
+++ cs/buildbot/trunk/contrib/svn_buildbot.py	2007-01-25 22:53:26 UTC (rev 5895)
@@ -119,25 +119,29 @@
 #  ...
 #
 
-def split_file_branches(changed_file):
+def split_file_root_branches(changed_file):
     filename = changed_file.split(os.sep)
+    rootname = []
     branchname = []
     while filename:
        piece = filename.pop(0)
-       branchname.append(piece)
        if piece == 'trunk':
+           branchname.append(piece)
            break
        elif piece == 'branches' or piece == 'tags':
+           branchname.append(piece)
            piece = filename.pop(0)
            branchname.append(piece)
            break
+       rootname.append(piece)
     if not filename:
         raise RuntimeError("cannot determine branch for '%s'" % changed_file)
+    rootname = os.sep.join(rootname) + os.sep
     branchname = os.sep.join(branchname)
     filename = os.sep.join(filename)
-    return (branchname, filename)
+    return (rootname, branchname, filename)
 
-split_file = split_file_branches
+split_file = split_file_root_branches
 
 
 class ChangeSender:
@@ -185,10 +189,11 @@
     """ % (fltpat, expat)
             sys.exit(0)
 
-        # now see which branches are involved
-        files_per_branch = {}
+        # now see which roots and branches are involved
+        branches_per_root = {}
         for f in changed:
-            branch, filename = split_file(f)
+            root, branch, filename = split_file(f)
+            files_per_branch = branches_per_root.setdefault(root, {})
             if files_per_branch.has_key(branch):
                 files_per_branch[branch].append(filename)
             else:
@@ -196,13 +201,16 @@
 
         # now create the Change dictionaries
         changes = []
-        for branch in files_per_branch.keys():
-            d = {'who': who,
-                 'branch': branch,
-                 'files': files_per_branch[branch],
-                 'comments': message,
-                 'revision': revision}
-            changes.append(d)
+        for root in branches_per_root.keys():
+            files_per_branch = branches_per_root[root]
+            for branch in files_per_branch.keys():
+                d = {'who': who,
+                     'root': root,
+                     'branch': branch,
+                     'files': files_per_branch[branch],
+                     'comments': message,
+                     'revision': revision}
+                changes.append(d)
 
         return changes
 



More information about the cig-commits mailing list