Changelog update.
[debian_buildbot.git] / master / buildbot / sourcestamp.py
blob988bfdfde833c94a5ad3823b5197c79c0a9bdb59
1 # This file is part of Buildbot. Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
8 # details.
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
14 # Copyright Buildbot Team Members
17 from zope.interface import implements
18 from twisted.persisted import styles
19 from buildbot import util, interfaces
21 class SourceStamp(util.ComparableMixin, styles.Versioned):
22 """This is a tuple of (branch, revision, patchspec, changes, project, repository).
24 C{branch} is always valid, although it may be None to let the Source
25 step use its default branch. There are three possibilities for the
26 remaining elements:
27 - (revision=REV, patchspec=None, changes=None): build REV. If REV is
28 None, build the HEAD revision from the given branch. Note that REV
29 must always be a string: SVN, Perforce, and other systems which use
30 integers should provide a string here, but the Source checkout step
31 will integerize it when making comparisons.
32 - (revision=REV, patchspec=(LEVEL, DIFF), changes=None): checkout REV,
33 then apply a patch to the source, with C{patch -pPATCHLEVEL <DIFF}.
34 If REV is None, checkout HEAD and patch it.
35 - (revision=None, patchspec=None, changes=[CHANGES]): let the Source
36 step check out the latest revision indicated by the given Changes.
37 CHANGES is a tuple of L{buildbot.changes.changes.Change} instances,
38 and all must be on the same branch.
39 """
41 persistenceVersion = 2
42 persistenceForgets = ( 'wasUpgraded', )
44 # all six of these are publically visible attributes
45 branch = None
46 revision = None
47 patch = None
48 changes = ()
49 project = ''
50 repository = ''
51 ssid = None # filled in by db.get_sourcestampid()
53 compare_attrs = ('branch', 'revision', 'patch', 'changes', 'project', 'repository')
55 implements(interfaces.ISourceStamp)
57 def __init__(self, branch=None, revision=None, patch=None,
58 changes=None, project='', repository=''):
59 if patch is not None:
60 assert len(patch) == 2
61 assert int(patch[0]) != -1
62 self.branch = branch
63 self.patch = patch
64 self.project = project
65 self.repository = repository
66 if changes:
67 self.changes = tuple(changes)
68 # set branch and revision to most recent change
69 self.branch = changes[-1].branch
70 revision = changes[-1].revision
71 if not self.project and hasattr(changes[-1], 'project'):
72 self.project = changes[-1].project
73 if not self.repository and hasattr(changes[-1], 'repository'):
74 self.repository = changes[-1].repository
76 if revision is not None:
77 if isinstance(revision, int):
78 revision = str(revision)
80 self.revision = revision
82 def canBeMergedWith(self, other):
83 if other.repository != self.repository:
84 return False
85 if other.branch != self.branch:
86 return False # the builds are completely unrelated
87 if other.project != self.project:
88 return False
90 if self.changes and other.changes:
91 # TODO: consider not merging these. It's a tradeoff between
92 # minimizing the number of builds and obtaining finer-grained
93 # results.
94 return True
95 elif self.changes and not other.changes:
96 return False # we're using changes, they aren't
97 elif not self.changes and other.changes:
98 return False # they're using changes, we aren't
100 if self.patch or other.patch:
101 return False # you can't merge patched builds with anything
102 if self.revision == other.revision:
103 # both builds are using the same specific revision, so they can
104 # be merged. It might be the case that revision==None, so they're
105 # both building HEAD.
106 return True
108 return False
110 def mergeWith(self, others):
111 """Generate a SourceStamp for the merger of me and all the other
112 SourceStamps. This is called by a Build when it starts, to figure
113 out what its sourceStamp should be."""
115 # either we're all building the same thing (changes==None), or we're
116 # all building changes (which can be merged)
117 changes = []
118 changes.extend(self.changes)
119 for ss in others:
120 changes.extend(ss.changes)
121 newsource = SourceStamp(branch=self.branch,
122 revision=self.revision,
123 patch=self.patch,
124 project=self.project,
125 repository=self.repository,
126 changes=changes)
127 return newsource
129 def getAbsoluteSourceStamp(self, got_revision):
130 return SourceStamp(branch=self.branch, revision=got_revision,
131 patch=self.patch, repository=self.repository,
132 project=self.project, changes=self.changes)
134 def getText(self):
135 # note: this won't work for VC systems with huge 'revision' strings
136 text = []
137 if self.project:
138 text.append("for %s" % self.project)
139 if self.repository:
140 text.append("in %s" % self.repository)
141 if self.revision is None:
142 return text + [ "latest" ]
143 text.append(str(self.revision))
144 if self.branch:
145 text.append("in '%s'" % self.branch)
146 if self.patch:
147 text.append("[patch]")
148 return text
150 def asDict(self):
151 result = {}
152 # Constant
153 result['revision'] = self.revision
154 # TODO(maruel): Make the patch content a suburl.
155 result['hasPatch'] = self.patch is not None
156 result['branch'] = self.branch
157 result['changes'] = [c.asDict() for c in getattr(self, 'changes', [])]
158 result['project'] = self.project
159 result['repository'] = self.repository
160 return result
162 def upgradeToVersion1(self):
163 # version 0 was untyped; in version 1 and later, types matter.
164 if self.branch is not None and not isinstance(self.branch, str):
165 self.branch = str(self.branch)
166 if self.revision is not None and not isinstance(self.revision, str):
167 self.revision = str(self.revision)
168 if self.patch is not None:
169 self.patch = ( int(self.patch[0]), str(self.patch[1]) )
170 self.wasUpgraded = True
172 def upgradeToVersion2(self):
173 # version 1 did not have project or repository; just set them to a default ''
174 self.project = ''
175 self.repository = ''
176 self.wasUpgraded = True
178 # vim: set ts=4 sts=4 sw=4 et: