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