1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from twisted.python import log
18 from buildbot.process.buildstep import LoggingBuildStep
19 from buildbot.status.builder import SKIPPED, FAILURE
20 from buildbot.steps.slave import CompositeStepMixin
21
22 -class Source(LoggingBuildStep, CompositeStepMixin):
23 """This is a base class to generate a source tree in the buildslave.
24 Each version control system has a specialized subclass, and is expected
25 to override __init__ and implement computeSourceRevision() and
26 startVC(). The class as a whole builds up the self.args dictionary, then
27 starts a RemoteCommand with those arguments.
28 """
29
30 renderables = LoggingBuildStep.renderables + [
31 'description', 'descriptionDone', 'descriptionSuffix',
32 'workdir' ]
33
34 description = None
35 descriptionDone = None
36 descriptionSuffix = None
37
38
39 haltOnFailure = True
40 flunkOnFailure = True
41 notReally = False
42
43 branch = None
44
45 - def __init__(self, workdir=None, mode='update', alwaysUseLatest=False,
46 timeout=20*60, retry=None, env=None, logEnviron=True,
47 description=None, descriptionDone=None, descriptionSuffix=None,
48 codebase='', **kwargs):
49 """
50 @type workdir: string
51 @param workdir: local directory (relative to the Builder's root)
52 where the tree should be placed
53
54 @type alwaysUseLatest: boolean
55 @param alwaysUseLatest: whether to always update to the most
56 recent available sources for this build.
57
58 Normally the Source step asks its Build for a list of all
59 Changes that are supposed to go into the build, then computes a
60 'source stamp' (revision number or timestamp) that will cause
61 exactly that set of changes to be present in the checked out
62 tree. This is turned into, e.g., 'cvs update -D timestamp', or
63 'svn update -r revnum'. If alwaysUseLatest=True, bypass this
64 computation and always update to the latest available sources
65 for each build.
66
67 The source stamp helps avoid a race condition in which someone
68 commits a change after the master has decided to start a build
69 but before the slave finishes checking out the sources. At best
70 this results in a build which contains more changes than the
71 buildmaster thinks it has (possibly resulting in the wrong
72 person taking the blame for any problems that result), at worst
73 is can result in an incoherent set of sources (splitting a
74 non-atomic commit) which may not build at all.
75
76 @type logEnviron: boolean
77 @param logEnviron: If this option is true (the default), then the
78 step's logfile will describe the environment
79 variables on the slave. In situations where the
80 environment is not relevant and is long, it may
81 be easier to set logEnviron=False.
82
83 @type codebase: string
84 @param codebase: Specifies which changes in a build are processed by
85 the step. The default codebase value is ''. The codebase must correspond
86 to a codebase assigned by the codebaseGenerator. If no codebaseGenerator
87 is defined in the master then codebase doesn't need to be set, the
88 default value will then match all changes.
89 """
90
91 LoggingBuildStep.__init__(self, **kwargs)
92
93
94 self.workdir = workdir
95
96 self.sourcestamp = None
97
98 self.codebase = codebase
99 if self.codebase:
100 self.name = ' '.join((self.name, self.codebase))
101
102 self.alwaysUseLatest = alwaysUseLatest
103
104 self.logEnviron = logEnviron
105 self.env = env
106 self.timeout = timeout
107
108 descriptions_for_mode = {
109 "clobber": "checkout",
110 "export": "exporting"}
111 descriptionDones_for_mode = {
112 "clobber": "checkout",
113 "export": "export"}
114 if description:
115 self.description = description
116 else:
117 self.description = [
118 descriptions_for_mode.get(mode, "updating")]
119 if isinstance(self.description, str):
120 self.description = [self.description]
121
122 if descriptionDone:
123 self.descriptionDone = descriptionDone
124 else:
125 self.descriptionDone = [
126 descriptionDones_for_mode.get(mode, "update")]
127 if isinstance(self.descriptionDone, str):
128 self.descriptionDone = [self.descriptionDone]
129
130 if descriptionSuffix:
131 self.descriptionSuffix = descriptionSuffix
132 else:
133 self.descriptionSuffix = self.codebase or None
134 if isinstance(self.descriptionSuffix, str):
135 self.descriptionSuffix = [self.descriptionSuffix]
136
138 """
139 Update a property, indexing the property by codebase if codebase is not
140 ''. Source steps should generally use this instead of setProperty.
141 """
142
143 if source == '':
144 source = self.__class__.__name__
145
146 if self.codebase != '':
147 assert not isinstance(self.getProperty(name, None), str), \
148 "Sourcestep %s has a codebase, other sourcesteps don't" \
149 % self.name
150 property_dict = self.getProperty(name, {})
151 property_dict[self.codebase] = value
152 LoggingBuildStep.setProperty(self, name, property_dict, source)
153 else:
154 assert not isinstance(self.getProperty(name, None), dict), \
155 "Sourcestep %s does not have a codebase, other sourcesteps do" \
156 % self.name
157 LoggingBuildStep.setProperty(self, name, value, source)
158
161
164
171
173 """Each subclass must implement this method to do something more
174 precise than -rHEAD every time. For version control systems that use
175 repository-wide change numbers (SVN, P4), this can simply take the
176 maximum such number from all the changes involved in this build. For
177 systems that do not (CVS), it needs to create a timestamp based upon
178 the latest Change, the Build's treeStableTimer, and an optional
179 self.checkoutDelay value."""
180 return None
181
183 if self.notReally:
184 log.msg("faking %s checkout/update" % self.name)
185 self.step_status.setText(["fake", self.name, "successful"])
186 self.addCompleteLog("log",
187 "Faked %s checkout/update 'successful'\n" \
188 % self.name)
189 return SKIPPED
190
191 if not self.alwaysUseLatest:
192
193 s = self.build.getSourceStamp(self.codebase)
194 self.sourcestamp = s
195
196 if self.sourcestamp:
197
198 branch = s.branch or self.branch
199
200 revision = s.revision
201 if not revision:
202 revision = self.computeSourceRevision(s.changes)
203
204
205 if revision is not None:
206 self.updateSourceProperty('revision', str(revision))
207
208
209
210
211
212 patch = s.patch
213 if patch:
214 self.addCompleteLog("patch", patch[1])
215 else:
216 log.msg("No sourcestamp found in build for codebase '%s'" % self.codebase)
217 self.step_status.setText(["Codebase", '%s' % self.codebase ,"not", "in", "build" ])
218 self.addCompleteLog("log",
219 "No sourcestamp found in build for codebase '%s'" \
220 % self.codebase)
221 self.finished(FAILURE)
222 return FAILURE
223
224 else:
225 revision = None
226 branch = self.branch
227 patch = None
228
229 self.startVC(branch, revision, patch)
230