]> git.sur5r.net Git - i3/i3.github.io/blob - docs/buildbot.html
update docs for v4.7
[i3/i3.github.io] / docs / buildbot.html
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
2     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
4 <head>\r
5 <link rel="icon" type="image/png" href="/favicon.png">\r
6 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
7 <meta name="generator" content="AsciiDoc 8.6.7" />\r
8 <title>i3: The i3 buildbot setup</title>\r
9 <link rel="stylesheet" href="/css/style.css" type="text/css" />\r
10 <link rel="stylesheet" href="/css/xhtml11.css" type="text/css" />\r
11 <script type="text/javascript">\r
12 /*<![CDATA[*/\r
13 window.onload = function(){asciidoc.footnotes(); asciidoc.toc(2);}\r
14 /*]]>*/\r
15 </script>\r
16 <script type="text/javascript" src="/js/asciidoc-xhtml11.js"></script>\r
17 </head>\r
18 <body class="article">\r
19 \r
20         <div id="main">\r
21             <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>\r
22                         <ul id="nav">\r
23                                 <li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>\r
24                                 <li><a href="/screenshots">Screens</a></li>\r
25                                 <li><a href="/contact">Contact</a></li>\r
26                                 <li><a href="http://bugs.i3wm.org/">Bugs</a></li>\r
27                         </ul>\r
28         <br style="clear: both">\r
29 <div id="content">\r
30 <div id="header">\r
31 <h1>The i3 buildbot setup</h1>\r
32 <span id="author">Michael Stapelberg</span><br />\r
33 <span id="email"><tt>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</tt></span><br />\r
34 <span id="revdate">September 2012</span>\r
35 <div id="toc">
36   <div id="toctitle">Table of Contents</div>
37   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
38 </div>\r
39 </div>\r
40 <div id="preamble">\r
41 <div class="sectionbody">\r
42 <div class="paragraph"><p>This document explains the <a href="http://www.buildbot.net/">buildbot</a> setup we use to\r
43 provide up-to-date documentation and debian packages at <a href="http://build.i3wm.org/">http://build.i3wm.org/</a>.\r
44 We publish these information so that our setup is well-documented (thus\r
45 decreasing future maintenance effort) and because it might be interesting for\r
46 other projects.</p></div>\r
47 </div>\r
48 </div>\r
49 <div class="sect1">\r
50 <h2 id="_introduction">1. Introduction</h2>\r
51 <div class="sectionbody">\r
52 <div class="paragraph"><p>What we are doing in i3 is called Continuous Integration (see\r
53 <a href="http://en.wikipedia.org/wiki/Continuous_integration">http://en.wikipedia.org/wiki/Continuous_integration</a>): we publish the changes we\r
54 make on our local machines as often as possible. In order to maintain a\r
55 continuously high quality, each time any developer pushes changes to the\r
56 official git repository, a number of quality assurance tools start running\r
57 automatically:</p></div>\r
58 <div class="olist arabic"><ol class="arabic">\r
59 <li>\r
60 <p>\r
61 Latest documentation is generated and provided at\r
62    <a href="http://build.i3wm.org/docs/">http://build.i3wm.org/docs/</a>. This makes it easy to link to documentation for\r
63    features which are only in the current git version, not in the released\r
64    version.\r
65 </p>\r
66 </li>\r
67 <li>\r
68 <p>\r
69 The source code is compiled and it is automatically posted to the IRC\r
70    channel whether there were any compiler warnings. While developers should\r
71    notice compiler warnings, this mechanism creates a bit of public pressure\r
72    ("Oh, Michael introduced warnings with this commit!"). More importantly,\r
73    since this mechanism builds a dist tarball and then compiles that tarball,\r
74    any changes to the source which would result in an uncompilable dist tarball\r
75    are immediately obvious. Therefore, we could cut a release from the current\r
76    git version at any point in time.\r
77 </p>\r
78 </li>\r
79 <li>\r
80 <p>\r
81 The clang static analyzer runs and the resulting report is provided at\r
82    <a href="http://build.i3wm.org/clang-analyze/">http://build.i3wm.org/clang-analyze/</a>. While every developer needs to compile\r
83    his code before committing, he doesn’t necessarily use clang (so we catch\r
84    build failures when using clang) and he also probably doesn’t run a static\r
85    analyzer as part of his normal workflow. By just being available without any\r
86    friction, this mechanism encourages developers to look at the report and fix\r
87    problems.\r
88 </p>\r
89 </li>\r
90 <li>\r
91 <p>\r
92 Debian (and Ubuntu) packages are built. This not only ensures that we don’t\r
93    change anything in the source code which would lead to an FTBFS (Fails To\r
94    Build From Source) when building a Debian package, it also goes a long way\r
95    to encourage end users to test i3. To remove the need and resource\r
96    requirements for them to compile their own version of i3 regularly, we\r
97    provide packages that integrate conveniently with a normal Debian system\r
98    (e.g. that are automatically upgraded).\r
99 </p>\r
100 </li>\r
101 </ol></div>\r
102 </div>\r
103 </div>\r
104 <div class="sect1">\r
105 <h2 id="_why_buildbot">2. Why buildbot?</h2>\r
106 <div class="sectionbody">\r
107 <div class="paragraph"><p>Previously, I was unsatisfied with the current state of FOSS CI tools like\r
108 Jenkins, Tinderbox and others. They either seemed bloated, hard to use,\r
109 outdated or unappealing for some other reason.</p></div>\r
110 <div class="paragraph"><p>Then I discovered buildbot and was impressed by its flexibility. It let me\r
111 implement everything I wanted from a CI tool and (in my opinion) it is\r
112 light-weight, easy to deploy and well maintained.</p></div>\r
113 <div class="paragraph"><p>The only downside of buildbot is its configuration and documentation: You need\r
114 to spend quite a bit of time (I needed multiple days) until it works the way\r
115 you want it to and oftentimes, the documentation is far too sparse. This is one\r
116 of the reasons why I’m publishing the i3 setup.</p></div>\r
117 </div>\r
118 </div>\r
119 <div class="sect1">\r
120 <h2 id="_configuration">3. Configuration</h2>\r
121 <div class="sectionbody">\r
122 <div class="paragraph"><p>See the next section for a complete, copy &amp; pasteable configuration file. This\r
123 section covers the most important aspects without covering every line.</p></div>\r
124 <div class="paragraph"><p>This document assumes you are running buildbot 0.8.6p1.</p></div>\r
125 <div class="sect2">\r
126 <h3 id="_change_sources">3.1. Change sources</h3>\r
127 <div class="paragraph"><p>Since i3 uses a central git repository, we use the official buildbot\r
128 <a href="https://github.com/buildbot/buildbot/blob/master/master/contrib/git_buildbot.py">git\r
129 post-receive hook</a> that sends the change information to the buildbot master.</p></div>\r
130 </div>\r
131 <div class="sect2">\r
132 <h3 id="_schedulers">3.2. Schedulers</h3>\r
133 <div class="paragraph"><p>There are two things (called "builders" in buildbot-language) which happen\r
134 whenever a new change in the <tt>next</tt> branch of i3 occurs:</p></div>\r
135 <div class="olist arabic"><ol class="arabic">\r
136 <li>\r
137 <p>\r
138 The "docs" builder builds and uploads the latest documentation. This happens\r
139    directly from the git repository with a custom asciidoc configuration which\r
140    indicates that these docs refer to the git version. Therefore, this builder\r
141    does not benefit from having a dist tarball available (contrary to the other\r
142    builders).\r
143 </p>\r
144 </li>\r
145 <li>\r
146 <p>\r
147 The "dist" builder prepares a dist tarball and then triggers the remaining\r
148    builders. This ensures that building the dist tarball (an operation which\r
149    takes about one minute due to documentation generation) only happens once.\r
150 </p>\r
151 </li>\r
152 </ol></div>\r
153 <div class="paragraph"><p>Here is the relevant configuration part:</p></div>\r
154 <div class="paragraph"><p><strong>Schedulers</strong>:</p></div>\r
155 <div class="listingblock">\r
156 <div class="content">\r
157 <pre><tt>c['schedulers'] = []\r
158 \r
159 c['schedulers'].append(SingleBranchScheduler(\r
160     name = 'dist',\r
161     branch = 'next',\r
162     treeStableTimer = 10,\r
163     builderNames = [ 'dist', 'docs' ],\r
164 ))\r
165 \r
166 c['schedulers'].append(Triggerable(\r
167     name = 'dist-tarball-done',\r
168     builderNames = [ 'compile', 'clang-analyze', 'debian-packages', 'ubuntu-packages' ],\r
169 ))</tt></pre>\r
170 </div></div>\r
171 </div>\r
172 <div class="sect2">\r
173 <h3 id="_building_the_dist_tarball">3.3. Building the dist tarball</h3>\r
174 <div class="paragraph"><p>This builder clones the i3 git repository and runs "make dist", which creates a\r
175 tarball that could be named "i3-4.2.tar.bz2" for example. This tarball is then\r
176 renamed to dist-%(gitversion).tar.bz2 (so that we can work with a predictable\r
177 name in the next steps) and uploaded to the buildbot master (since we can have\r
178 multiple buildslaves, we cannot just let it rest on the buildslave that built\r
179 it). Afterwards, old dist tarballs are cleaned up and the remaining builders\r
180 are triggered:</p></div>\r
181 <div class="paragraph"><p><strong>Building a dist tarball</strong>:</p></div>\r
182 <div class="listingblock">\r
183 <div class="content">\r
184 <pre><tt>factories = {}\r
185 \r
186 f = factories['dist'] = BuildFactory()\r
187 \r
188 # Check out the git repository.\r
189 f.addStep(s_git)\r
190 \r
191 # Fill the 'gitversion' property with the output of git describe --tags.\r
192 f.addStep(shell.SetProperty(command = 'git describe --tags', property = 'gitversion'))\r
193 \r
194 # Build the dist tarball.\r
195 cmd(f, name = 'make dist', command = [ 'make', 'dist' ])\r
196 \r
197 # Rename the created tarball to a well-known name.\r
198 cmd(f,\r
199     name = 'rename tarball',\r
200     command = WithProperties('mv *.tar.bz2 dist-%(gitversion)s.tar.bz2'),\r
201 )\r
202 \r
203 # Upload the dist tarball to the master (other factories download it later).\r
204 f.addStep(transfer.FileUpload(\r
205     slavesrc = WithProperties('dist-%(gitversion)s.tar.bz2'),\r
206     masterdest = WithProperties('distballs/dist-%(gitversion)s.tar.bz2'),\r
207 ))\r
208 \r
209 # Cleanup old dist tarballs (everything older than tree days).\r
210 f.addStep(master.MasterShellCommand(\r
211     command = "find distballs -mtime +3 -exec rm '{}' \;",\r
212     name = 'cleanup old dist tarballs',\r
213 ))\r
214 \r
215 # Everything worked fine, now trigger compilation.\r
216 f.addStep(Trigger(\r
217     schedulerNames = [ 'dist-tarball-done' ],\r
218     copy_properties = [ 'gitversion' ],\r
219 ))</tt></pre>\r
220 </div></div>\r
221 <div class="paragraph"><p>Three things are noteworthy about this part of the configuration:</p></div>\r
222 <div class="olist arabic"><ol class="arabic">\r
223 <li>\r
224 <p>\r
225 For convenience, we call each factory <tt>f</tt> (just like the global buildbot\r
226    config uses <tt>c</tt> for the top-level configuration) and add it to a dictionary.\r
227    Factories in that dictionary are later automatically configured for each\r
228    buildslave.\r
229 </p>\r
230 </li>\r
231 <li>\r
232 <p>\r
233 We have a shared step called <tt>s_git</tt> so that we only have one location in\r
234    the configuration file where we specify the git repository URL and branch.\r
235 </p>\r
236 </li>\r
237 <li>\r
238 <p>\r
239 We have a custom function called <tt>cmd</tt> which is a shortcut for defining a\r
240    <tt>ShellCommand</tt> with <tt>haltOnFailure=True</tt> (since each step is critical) and\r
241    <tt>logEnviron=False</tt> (for brevity).\r
242 </p>\r
243 </li>\r
244 </ol></div>\r
245 <div class="paragraph"><p>Here are their definitions:</p></div>\r
246 <div class="paragraph"><p><strong>cmd</strong>:</p></div>\r
247 <div class="listingblock">\r
248 <div class="content">\r
249 <pre><tt>def cmd(factory, **kwargs):\r
250     factory.addStep(ShellCommand(\r
251         haltOnFailure = True,\r
252         logEnviron = False,\r
253         **kwargs\r
254     ))</tt></pre>\r
255 </div></div>\r
256 <div class="paragraph"><p><strong>s_git</strong>:</p></div>\r
257 <div class="listingblock">\r
258 <div class="content">\r
259 <pre><tt>s_git = Git(\r
260     repourl = 'git://code.i3wm.org/i3',\r
261     branch = 'next',\r
262 \r
263     # Check out the latest revision, not the one which caused this build.\r
264     alwaysUseLatest = True,\r
265 \r
266     # We cannot use shallow because it breaks git describe --tags.\r
267     shallow = False,\r
268 \r
269     # Delete remnants of previous builds.\r
270     mode = 'full',\r
271 \r
272     # Store checkouts in source/ and copy them over to build/ to save\r
273     # bandwidth.\r
274     method = 'copy',\r
275 )</tt></pre>\r
276 </div></div>\r
277 </div>\r
278 <div class="sect2">\r
279 <h3 id="_compiling_the_dist_tarball">3.4. Compiling the dist tarball</h3>\r
280 <div class="paragraph"><p>For this builder to work, you obviously need to install all the\r
281 build-dependencies for your software on each buildslave. In the case of i3,\r
282 this can be done with <tt>apt-get build-dep i3-wm</tt>.</p></div>\r
283 <div class="paragraph"><p>The compilation is pretty straight-forward since it uses the builtin <tt>Compile</tt>\r
284 step. We call <tt>make</tt> with <tt>-j4</tt> (we don’t have enough buildslaves to make\r
285 figuring out the amount of cores at build-time worthwhile) and <tt>DEBUG=0</tt> to\r
286 simulate release build conditions. Also, we pass the preprocessor flag\r
287 <tt>-D_FORTIFY_SOURCE=2</tt> and the compiler flags <tt>-Wformat</tt> and <tt>-Wformat-security</tt>\r
288 to enable additional warnings.</p></div>\r
289 <div class="paragraph"><p><strong>Compiling the dist tarball</strong>:</p></div>\r
290 <div class="listingblock">\r
291 <div class="content">\r
292 <pre><tt>f = factories['compile'] = BuildFactory()\r
293 unpack_dist_tarball(f)\r
294 f.addStep(Compile(\r
295     command = [ 'make', 'DEBUG=0', '-j4' ],\r
296     warningPattern = '.*warning: ',\r
297     warnOnWarnings = True,\r
298     workdir = 'build/DIST',\r
299     env = {\r
300       'CPPFLAGS': '-D_FORTIFY_SOURCE=2',\r
301       'CFLAGS': '-Wformat -Wformat-security'\r
302     },\r
303 ))\r
304 \r
305 f.addStep(WarningsToIRC())</tt></pre>\r
306 </div></div>\r
307 <div class="paragraph"><p>Again, we use custom functions (and a custom buildstep) to make our lives\r
308 easier. Here is the definition of unpack_dist_tarball which adds three steps to\r
309 the factory that download and unpack the dist tarball to the <tt>DIST/</tt> directory:</p></div>\r
310 <div class="paragraph"><p><strong>unpack_dist_tarball</strong>:</p></div>\r
311 <div class="listingblock">\r
312 <div class="content">\r
313 <pre><tt>def unpack_dist_tarball(factory):\r
314     factory.addStep(transfer.FileDownload(\r
315         mastersrc = WithProperties('distballs/dist-%(gitversion)s.tar.bz2'),\r
316         slavedest = 'dist.tar.bz2',\r
317     ))\r
318 \r
319     factory.addStep(slave.MakeDirectory(dir = 'build/DIST'))\r
320 \r
321     cmd(factory,\r
322         name = 'unpack dist tarball',\r
323         command = [ 'tar', 'xf', 'dist.tar.bz2', '-C', 'DIST', '--strip-components=1' ],\r
324     )</tt></pre>\r
325 </div></div>\r
326 <div class="paragraph"><p>The <tt>WarningsToIRC</tt> build step is a custom build step which sets a property\r
327 called "ircsuffix" that is used by our custom IRC bot. This is covered later in\r
328 more detail. This property gets set to a green or red message, depending on\r
329 whether there were any warnings:</p></div>\r
330 <div class="paragraph"><p><strong>WarningsToIRC</strong>:</p></div>\r
331 <div class="listingblock">\r
332 <div class="content">\r
333 <pre><tt>class WarningsToIRC(buildstep.BuildStep):\r
334     def start(self):\r
335         warnings = self.getProperty("warnings-count")\r
336         if warnings is not None and int(warnings) &gt; 0:\r
337             warnings = int(warnings)  # just to be sure\r
338             self.setProperty("ircsuffix", ("\0037 with %d warning%s!" %\r
339                 (warnings, "s" if warnings != 1 else "")))\r
340         else:\r
341             self.setProperty("ircsuffix", "\0033 without warnings")\r
342         self.finished(SUCCESS)</tt></pre>\r
343 </div></div>\r
344 </div>\r
345 <div class="sect2">\r
346 <h3 id="_static_code_analysis">3.5. Static code analysis</h3>\r
347 <div class="paragraph"><p>For this builder to work, you additionally need the <tt>clang</tt> compiler on each\r
348 buildslave: <tt>apt-get install clang</tt>.</p></div>\r
349 <div class="paragraph"><p>This builder uses only custom functions which you already know by now. It runs\r
350 scan-build, then moves scan-build’s output from a date-based directory directly\r
351 into the <tt>CLANG/</tt> directory and uploads that to the buildmaster.</p></div>\r
352 <div class="paragraph"><p>On the buildmaster, a webserver is configured which has a symlink to\r
353 <tt>/home/build/i3-master/htdocs/clang-analyze</tt> in its document root.</p></div>\r
354 <div class="paragraph"><p><strong>static code analysis</strong>:</p></div>\r
355 <div class="listingblock">\r
356 <div class="content">\r
357 <pre><tt>f = factories['clang-analyze'] = BuildFactory()\r
358 unpack_dist_tarball(f)\r
359 cmd(f,\r
360     name='analyze',\r
361     command = [\r
362         'scan-build',\r
363         '-o', '../CLANG',\r
364         '--html-title', WithProperties('Analysis of i3 v%(gitversion)s'),\r
365         'make', '-j8',\r
366     ],\r
367     workdir = 'build/DIST',\r
368 )\r
369 \r
370 # remove the subdirectory -- we always want to overwrite\r
371 cmd(f, command = 'mv CLANG/*/* CLANG/')\r
372 \r
373 f.addStep(transfer.DirectoryUpload(\r
374     slavesrc = 'CLANG',\r
375     masterdest = 'htdocs/clang-analyze',\r
376     compress = 'bz2',\r
377     name = 'upload output',\r
378 ))\r
379 \r
380 f.addStep(ClangToIRC())</tt></pre>\r
381 </div></div>\r
382 <div class="paragraph"><p>The <tt>ClangToIRC</tt> custom step is even simpler than <tt>WarningsToIRC</tt>. It simply\r
383 sets the ircsuffix property to a static message:</p></div>\r
384 <div class="paragraph"><p><strong>ClangToIRC</strong>:</p></div>\r
385 <div class="listingblock">\r
386 <div class="content">\r
387 <pre><tt>class ClangToIRC(buildstep.BuildStep):\r
388     def start(self):\r
389         self.setProperty("ircsuffix", ", see http://build.i3wm.org/clang-analyze/")\r
390         self.finished(SUCCESS)</tt></pre>\r
391 </div></div>\r
392 </div>\r
393 <div class="sect2">\r
394 <h3 id="_generating_documentation">3.6. Generating documentation</h3>\r
395 <div class="paragraph"><p>This builder is the one which is the least clean of all. It uses the Debian\r
396 packaging information to decide which docs to publish and which manpages to\r
397 generate. Additionally, it uses a for loop instead of calling a script. I\r
398 recommend including a script to do this in your repository instead.</p></div>\r
399 <div class="paragraph"><p>Apart from these concerns, the builder is straight-forward: It clones the git\r
400 repository, generates the documentation and then uploads the documentation to\r
401 the buildmaster:</p></div>\r
402 <div class="paragraph"><p><strong>Generating documentation</strong>:</p></div>\r
403 <div class="listingblock">\r
404 <div class="content">\r
405 <pre><tt>f = factories['docs'] = BuildFactory()\r
406 f.addStep(s_git)\r
407 # Fill the 'gitversion' property with the output of git describe --tags.\r
408 f.addStep(shell.SetProperty(command = 'git describe --tags', property = 'gitversion'))\r
409 cmd(f, name = 'build docs', command = [ 'make', '-C', 'docs', "ASCIIDOC=asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf" ])\r
410 cmd(f, name = 'build manpages', command = "for file in $(sed 's/\.1$/.man/g' debian/i3-wm.manpages); do asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf \"$file\"; done")\r
411 f.addStep(slave.MakeDirectory(dir='build/COPY-DOCS'))\r
412 cmd(f, name = 'copy docs', command = "cp $(tr '\\n' ' ' &lt; debian/i3-wm.docs) COPY-DOCS")\r
413 cmd(f, name = 'copy manpages', command = "cp $(sed 's/\.1$/.html/g' debian/i3-wm.manpages | tr '\\n' ' ') COPY-DOCS")\r
414 \r
415 f.addStep(transfer.DirectoryUpload(\r
416     slavesrc = 'COPY-DOCS',\r
417     masterdest = 'htdocs/docs-git',\r
418     compress = 'bz2',\r
419     name = 'upload docs'))\r
420 \r
421 f.addStep(DocsToIRC())</tt></pre>\r
422 </div></div>\r
423 <div class="paragraph"><p>Just as <tt>ClangToIRC</tt>, <tt>DocsToIRC</tt> appends a static message:</p></div>\r
424 <div class="paragraph"><p><strong>DocsToIRC</strong>:</p></div>\r
425 <div class="listingblock">\r
426 <div class="content">\r
427 <pre><tt>class DocsToIRC(buildstep.BuildStep):\r
428     def start(self):\r
429         self.setProperty("ircsuffix", ", see http://build.i3wm.org/docs/")\r
430         self.finished(SUCCESS)</tt></pre>\r
431 </div></div>\r
432 </div>\r
433 <div class="sect2">\r
434 <h3 id="_building_debian_ubuntu_packages">3.7. Building Debian/Ubuntu packages</h3>\r
435 <div class="paragraph"><p>This is the most complex builder of all. It uses <tt>pbuilder-dist</tt>, <tt>debchange</tt>,\r
436 <tt>dpkg-buildpackage</tt> and <tt>reprepro</tt> to generate a Debian repository with a\r
437 cleanly compiled package for amd64 and i386. In order for it to work, you need\r
438 to install the following packages: <tt>apt-get install devscripts dpkg-dev\r
439 reprepro ubuntu-dev-tools pbuilder</tt>. Afterwards, you need to allow the user as\r
440 which the buildslave runs to execute pbuilder via sudo without needing a\r
441 password, so add a config file like this one:</p></div>\r
442 <div class="paragraph"><p><strong>sudoers.d</strong>:</p></div>\r
443 <div class="listingblock">\r
444 <div class="content">\r
445 <pre><tt>echo 'build    ALL= NOPASSWD: SETENV: /usr/sbin/pbuilder' &gt; /etc/sudoers.d/build</tt></pre>\r
446 </div></div>\r
447 <div class="paragraph"><p>Then, as the user as which your buildslave runs, setup the pbuilder\r
448 environments (you only need to do this once):</p></div>\r
449 <div class="paragraph"><p><strong>pbuilder preparation</strong>:</p></div>\r
450 <div class="listingblock">\r
451 <div class="content">\r
452 <pre><tt>sudo ln -s pbuilder-dist /usr/bin/pbuilder-sid-amd64\r
453 sudo ln -s pbuilder-dist /usr/bin/pbuilder-sid-i386\r
454 pbuilder-sid-amd64 create\r
455 pbuilder-sid-i386 create</tt></pre>\r
456 </div></div>\r
457 <div class="paragraph"><p>Also, you will need a GPG key to sign these packages.</p></div>\r
458 <div class="paragraph"><p>The debian builder starts by unpacking the dist tarball, copying the Debian\r
459 packaging from git, creating an empty Debian repository with the\r
460 <tt>i3-autobuild-keyring</tt> contents in it. It then adds a new changelog entry to\r
461 reflect the git version and the fact that this package was built automatically,\r
462 builds a source package with <tt>dpkg-buildpackage</tt> and adds it to the repository.\r
463 Afterwards, it updates each pbuilder and builds binary packages for each\r
464 architecture (amd64 and i386). After adding the resulting packages to the\r
465 repository, it uploads the repository to the buildmaster:</p></div>\r
466 <div class="paragraph"><p><strong>Debian builder</strong>:</p></div>\r
467 <div class="listingblock">\r
468 <div class="content">\r
469 <pre><tt>distributions = [ 'sid-amd64', 'sid-i386' ]\r
470 gpg_key = 'BE1DB1F1'\r
471 \r
472 f = factories['debian-packages'] = BuildFactory()\r
473 # We need the git repository for the Debian packaging.\r
474 f.addStep(s_git)\r
475 unpack_dist_tarball(f)\r
476 cmd(f, name = 'copy packaging', command = "cp -r debian DIST/")\r
477 \r
478 # Add a new changelog entry to have the git version in the package version.\r
479 cmd(f,\r
480     name = 'update changelog',\r
481     workdir = 'build/DIST',\r
482     command = [ 'debchange', '-m', '-l', WithProperties('+g%(gitversion)s'), 'Automatically built' ],\r
483 )\r
484 \r
485 cmd(f,\r
486     name = 'source pkg',\r
487     command = [ 'dpkg-buildpackage', '-S', '-us', '-uc' ],\r
488     workdir = 'build/DIST',\r
489 )\r
490 \r
491 for dist in distributions:\r
492     f.addStep(slave.MakeDirectory(dir = 'build/RESULT-' + dist))\r
493 \r
494 # Create debian sid repository\r
495 f.addStep(slave.MakeDirectory(dir = 'build/REPO-sid/conf'))\r
496 f.addStep(transfer.StringDownload(\r
497     """Codename: sid\r
498 Suite: unstable\r
499 Architectures: i386 amd64 source\r
500 Components: main\r
501 DebIndices: Packages Release . .gz .bz2\r
502 DscIndices: Sources Release . .gz .bz2\r
503 SignWith: %(gpg_key)s\r
504 """ % { "gpg_key": gpg_key },\r
505     slavedest = 'REPO-sid/conf/distributions',\r
506 ))\r
507 \r
508 # add source package to repository\r
509 reprepro_include(f, 'i3-wm*_source.changes', 'dsc')\r
510 \r
511 # Add keyring to the repository. We need to run git clone on our own because\r
512 # the Git() step assumes there’s precisely one repository we want to deal with.\r
513 # No big deal since the i3-autobuild-keyring repository is not big.\r
514 cmd(f,\r
515     name = 'clone keyring repo',\r
516     command = 'git clone git://code.i3wm.org/i3-autobuild-keyring',\r
517 )\r
518 reprepro_include(f, 'i3-autobuild-keyring/prebuilt/*.changes')\r
519 \r
520 for dist in distributions:\r
521     # update the pbuilder\r
522     cmd(f, name = 'update builder', command = 'pbuilder-' + dist + ' update')\r
523 \r
524     # build the package for each dist\r
525     f.addStep(ShellCommand(\r
526         logEnviron = False,\r
527         name = 'pkg ' + dist,\r
528         command = 'pbuilder-' + dist + ' build --binary-arch \\r
529 --buildresult RESULT-' + dist + ' --debbuildopts -j8 i3-wm*dsc',\r
530         warnOnFailure = True\r
531     ))\r
532 \r
533     reprepro_include(f, 'RESULT-' + dist + '/*.changes')\r
534 \r
535 # upload the sid repo\r
536 # Since the next step is cleaning up old files, we set haltOnFailure=True -- we\r
537 # prefer providing old packages over providing no packages at all :).\r
538 for directory in [ 'pool', 'dists' ]:\r
539     f.addStep(transfer.DirectoryUpload(\r
540         slavesrc = 'REPO-sid/' + directory,\r
541         masterdest = 'htdocs/debian/sid/' + directory,\r
542         compress = 'bz2',\r
543         name = 'upload sid ' + directory,\r
544         haltOnFailure = True,\r
545     ))\r
546 \r
547 f.addStep(master.MasterShellCommand(\r
548     command = "find htdocs/debian/sid/pool -mtime +3 -exec rm '{}' \;",\r
549     name = 'cleanup old packages',\r
550 ))\r
551 \r
552 # We ensure there is an empty i18n/Index to speed up apt (so that it does not\r
553 # try to download Translation-*)\r
554 f.addStep(master.MasterShellCommand(\r
555     command = [ 'mkdir', '-p', 'htdocs/debian/sid/dists/sid/main/i18n' ],\r
556     name = 'create i18n folder',\r
557 ))\r
558 f.addStep(master.MasterShellCommand(\r
559     command = [ 'touch', 'htdocs/debian/sid/dists/sid/main/i18n/Index' ],\r
560     name = 'touch i18n/Index',\r
561 ))</tt></pre>\r
562 </div></div>\r
563 <div class="paragraph"><p>The <tt>reprepro_include</tt> command is defined as follows:</p></div>\r
564 <div class="paragraph"><p><strong>reprepro_include</strong>:</p></div>\r
565 <div class="listingblock">\r
566 <div class="content">\r
567 <pre><tt>def reprepro_include(factory, path, debtype='deb', **kwargs):\r
568     cmd(factory,\r
569         name = 'reprepro include',\r
570         command = 'reprepro --ignore=wrongdistribution -T ' + debtype + ' -b REPO-sid include sid ' + path,\r
571         **kwargs\r
572     )</tt></pre>\r
573 </div></div>\r
574 <div class="paragraph"><p>Running such a builder for Ubuntu works exactly the same way, but you need to\r
575 replace "sid" with "precise" in all places (see the full configuration file for\r
576 an example).</p></div>\r
577 </div>\r
578 <div class="sect2">\r
579 <h3 id="_status_targets">3.8. Status targets</h3>\r
580 <div class="paragraph"><p>We don’t advertise the HTTP status target. Instead, status is posted to IRC via\r
581 a custom bot. This bot provides an HTTP end point and buildbot is configured to\r
582 push status changes to that endpoint:</p></div>\r
583 <div class="paragraph"><p><strong>http status target</strong>:</p></div>\r
584 <div class="listingblock">\r
585 <div class="content">\r
586 <pre><tt>c['status'].append(buildbot.status.status_push.HttpStatusPush(\r
587     serverUrl = 'http://localhost:8080/push_buildbot',\r
588 ))</tt></pre>\r
589 </div></div>\r
590 <div class="paragraph"><p>You can find the source code of that bot at\r
591 <a href="http://code.stapelberg.de/git/go-buildbot-announce/">http://code.stapelberg.de/git/go-buildbot-announce/</a>. As the name suggests, it\r
592 is written in Go. Also, it is quite specific to i3, so you might be better off\r
593 implementing such a bot (or plugin) on your own. It might make for a nice\r
594 example, though, especially back when its only feature was announcing the build\r
595 status:</p></div>\r
596 <div class="paragraph"><p><a href="http://code.stapelberg.de/git/go-buildbot-announce/tree/src/i3build.go?id=eeebf1a546454c8a0d82ca623886bb835cd32ba0">http://code.stapelberg.de/git/go-buildbot-announce/tree/src/i3build.go?id=eeebf1a546454c8a0d82ca623886bb835cd32ba0</a></p></div>\r
597 </div>\r
598 <div class="sect2">\r
599 <h3 id="_creating_the_buildslave">3.9. Creating the buildslave</h3>\r
600 <div class="paragraph"><p>One more thing to note is that when creating the buildslave, you should use the\r
601 <tt>--umask</tt> argument to configure the umask for all generated files:</p></div>\r
602 <div class="paragraph"><p><strong>Creating the buildslave</strong>:</p></div>\r
603 <div class="listingblock">\r
604 <div class="content">\r
605 <pre><tt>buildslave create-slave --umask=022 i3-buildslave buildbot.i3wm.org build-1 &lt;password&gt;</tt></pre>\r
606 </div></div>\r
607 </div>\r
608 </div>\r
609 </div>\r
610 <div class="sect1">\r
611 <h2 id="_full_configuration_file">4. Full configuration file</h2>\r
612 <div class="sectionbody">\r
613 <div class="paragraph"><p>This is the full configuration file, as tested and currently in use (except for\r
614 the passwords, though):</p></div>\r
615 <div class="paragraph"><p><strong>master.cfg</strong>:</p></div>\r
616 <div class="listingblock">\r
617 <div class="content">\r
618 <pre><tt># -*- python -*-\r
619 # -*- coding: utf-8\r
620 # vim:ts=4:sw=4:expandtab:syntax=python\r
621 #\r
622 # i3 buildbot configuration\r
623 # © 2012 Michael Stapelberg, Public Domain\r
624 # see http://i3wm.org/docs/buildbot.html for more information.\r
625 \r
626 from buildbot.buildslave import BuildSlave\r
627 from buildbot.changes import pb\r
628 from buildbot.schedulers.basic import SingleBranchScheduler\r
629 from buildbot.schedulers.triggerable import Triggerable\r
630 from buildbot.process.properties import WithProperties\r
631 from buildbot.process.factory import BuildFactory\r
632 from buildbot.steps.source.git import Git\r
633 from buildbot.steps.shell import ShellCommand\r
634 from buildbot.steps.shell import Compile\r
635 from buildbot.steps.trigger import Trigger\r
636 from buildbot.steps import shell, transfer, master, slave\r
637 from buildbot.config import BuilderConfig\r
638 from buildbot.process import buildstep\r
639 from buildbot.status import html\r
640 from buildbot.status import words\r
641 import buildbot.status.status_push\r
642 from buildbot.status.web import auth, authz\r
643 from buildbot.status.builder import SUCCESS, FAILURE\r
644 \r
645 c = BuildmasterConfig = {}\r
646 \r
647 c['slaves'] = [BuildSlave('docsteel-vm', 'secret')]\r
648 c['slavePortnum'] = 9989\r
649 # Changes are pushed to buildbot using a git hook.\r
650 c['change_source'] = [pb.PBChangeSource(\r
651     user = 'i3-source',\r
652     passwd = 'secret',\r
653 )]\r
654 \r
655 ################################################################################\r
656 # schedulers\r
657 ################################################################################\r
658 \r
659 c['schedulers'] = []\r
660 \r
661 # The first scheduler kicks off multiple builders:\r
662 # • 'dist' builds a dist tarball and starts the triggerable schedulers\r
663 #   'compile'\r
664 # • 'docs' builds the documentation with a special asciidoc configuration\r
665 #   (therefore, it does not profit from a dist tarball and can be run in\r
666 #    parallel).\r
667 c['schedulers'].append(SingleBranchScheduler(\r
668     name = 'dist',\r
669     branch = 'next',\r
670     treeStableTimer = 10,\r
671     builderNames = [ 'dist', 'docs' ],\r
672 ))\r
673 \r
674 c['schedulers'].append(Triggerable(\r
675     name = 'dist-tarball-done',\r
676     builderNames = [ 'compile', 'clang-analyze', 'debian-packages', 'ubuntu-packages' ],\r
677 ))\r
678 \r
679 ################################################################################\r
680 # Shortcuts for builders\r
681 ################################################################################\r
682 \r
683 # shortcut for a ShellCommand with haltOnFailure=True, logEnviron=False\r
684 def cmd(factory, **kwargs):\r
685     factory.addStep(ShellCommand(\r
686         haltOnFailure=True,\r
687         logEnviron=False,\r
688         **kwargs\r
689     ))\r
690 \r
691 # Shortcut to add steps necessary to download and unpack the dist tarball.\r
692 def unpack_dist_tarball(factory):\r
693     factory.addStep(transfer.FileDownload(\r
694         mastersrc=WithProperties('distballs/dist-%(gitversion)s.tar.bz2'),\r
695         slavedest='dist.tar.bz2',\r
696     ))\r
697     factory.addStep(slave.MakeDirectory(dir='build/DIST'))\r
698     cmd(factory,\r
699         name = 'unpack dist tarball',\r
700         command = [ 'tar', 'xf', 'dist.tar.bz2', '-C', 'DIST', '--strip-components=1' ],\r
701     )\r
702 \r
703 # Includes the given path in REPO-sid using reprepro.\r
704 def reprepro_include(factory, path, debtype='deb', **kwargs):\r
705     cmd(factory,\r
706         name = 'reprepro include',\r
707         command = 'reprepro --ignore=wrongdistribution -T ' + debtype + ' -b REPO-sid include sid ' + path,\r
708         **kwargs\r
709     )\r
710 \r
711 def reprepro_include_ubuntu(factory, path, debtype='deb', **kwargs):\r
712     cmd(factory,\r
713         name = 'reprepro include',\r
714         command = 'reprepro --ignore=wrongdistribution -T ' + debtype + ' -b REPO-sid include precise ' + path,\r
715         **kwargs\r
716     )\r
717 \r
718 ################################################################################\r
719 # Custom steps\r
720 ################################################################################\r
721 \r
722 # Adds the ircsuffix property to reflect whether there were warnings.\r
723 class WarningsToIRC(buildstep.BuildStep):\r
724   def start(self):\r
725     warnings = self.getProperty("warnings-count")\r
726     if warnings is not None and int(warnings) &gt; 0:\r
727       warnings = int(warnings)  # just to be sure\r
728       self.setProperty("ircsuffix", "\0037 with %d warning%s!" % (warnings, "s" if warnings != 1 else ""))\r
729     else:\r
730       self.setProperty("ircsuffix", "\0033 without warnings")\r
731     self.finished(SUCCESS)\r
732 \r
733 # Adds a link to the automatically generated documentation.\r
734 class DocsToIRC(buildstep.BuildStep):\r
735   def start(self):\r
736     self.setProperty("ircsuffix", ", see http://build.i3wm.org/docs/")\r
737     self.finished(SUCCESS)\r
738 \r
739 # Adds a link to the clang report.\r
740 class ClangToIRC(buildstep.BuildStep):\r
741   def start(self):\r
742     self.setProperty("ircsuffix", ", see http://build.i3wm.org/clang-analyze/")\r
743     self.finished(SUCCESS)\r
744 \r
745 ################################################################################\r
746 # Shared steps, used in different factories.\r
747 ################################################################################\r
748 \r
749 s_git = Git(\r
750     repourl='git://code.i3wm.org/i3',\r
751     branch='next',\r
752 \r
753     # Check out the latest revision, not the one which caused this build.\r
754     alwaysUseLatest=True,\r
755 \r
756     # We cannot use shallow because it breaks git describe --tags.\r
757     shallow=False,\r
758 \r
759     # Delete remnants of previous builds.\r
760     mode='full',\r
761 \r
762     # Store checkouts in source/ and copy them over to build/ to save\r
763     # bandwidth.\r
764     method='copy',\r
765 \r
766     # XXX: In newer versions of buildbot (&gt; 0.8.6), we want to use\r
767     # getDescription={ 'tags': True } here and get rid of the extra git\r
768     # describe --tags step.\r
769 )\r
770 \r
771 ################################################################################\r
772 # factory: "dist" — builds the dist tarball once (used by all other factories)\r
773 ################################################################################\r
774 \r
775 factories = {}\r
776 \r
777 f = factories['dist'] = BuildFactory()\r
778 # Check out the git repository.\r
779 f.addStep(s_git)\r
780 # Fill the 'gitversion' property with the output of git describe --tags.\r
781 f.addStep(shell.SetProperty(command = 'git describe --tags', property = 'gitversion'))\r
782 # Build the dist tarball.\r
783 cmd(f, name = 'make dist', command = [ 'make', 'dist' ])\r
784 # Rename the created tarball to a well-known name.\r
785 cmd(f, name = 'rename tarball', command = WithProperties('mv *.tar.bz2 dist-%(gitversion)s.tar.bz2'))\r
786 # Upload the dist tarball to the master (other factories download it later).\r
787 f.addStep(transfer.FileUpload(\r
788     slavesrc = WithProperties('dist-%(gitversion)s.tar.bz2'),\r
789     masterdest = WithProperties('distballs/dist-%(gitversion)s.tar.bz2'),\r
790 ))\r
791 # Cleanup old dist tarballs (everything older than tree days).\r
792 f.addStep(master.MasterShellCommand(\r
793     command = "find distballs -mtime +3 -exec rm '{}' \;",\r
794     name = 'cleanup old dist tarballs',\r
795 ))\r
796 # Everything worked fine, now trigger compilation.\r
797 f.addStep(Trigger(\r
798     schedulerNames = [ 'dist-tarball-done' ],\r
799     copy_properties = [ 'gitversion' ],\r
800 ))\r
801 \r
802 ################################################################################\r
803 # factory: "compile" — compiles the dist tarball and reports warnings\r
804 ################################################################################\r
805 \r
806 f = factories['compile'] = BuildFactory()\r
807 unpack_dist_tarball(f)\r
808 f.addStep(Compile(\r
809     command = [ 'make', 'DEBUG=0', '-j4' ],\r
810     warningPattern = '.*warning: ',\r
811     warnOnWarnings = True,\r
812     workdir = 'build/DIST',\r
813     env = {\r
814       'CPPFLAGS': '-D_FORTIFY_SOURCE=2',\r
815       'CFLAGS': '-Wformat -Wformat-security'\r
816     },\r
817 ))\r
818 \r
819 f.addStep(WarningsToIRC())\r
820 \r
821 ################################################################################\r
822 # factory: "clang-analyze" — runs a static code analysis\r
823 ################################################################################\r
824 # $ sudo apt-get install clang\r
825 \r
826 f = factories['clang-analyze'] = BuildFactory()\r
827 unpack_dist_tarball(f)\r
828 cmd(f,\r
829     name='analyze',\r
830     command = [\r
831         'scan-build',\r
832         '-o', '../CLANG',\r
833         '--html-title', WithProperties('Analysis of i3 v%(gitversion)s'),\r
834         'make', '-j8',\r
835     ],\r
836     workdir = 'build/DIST',\r
837 )\r
838 \r
839 # remove the subdirectory -- we always want to overwrite\r
840 cmd(f, command = 'mv CLANG/*/* CLANG/')\r
841 \r
842 f.addStep(transfer.DirectoryUpload(\r
843     slavesrc = 'CLANG',\r
844     masterdest = 'htdocs/clang-analyze',\r
845     compress = 'bz2',\r
846     name = 'upload output',\r
847 ))\r
848 \r
849 f.addStep(ClangToIRC())\r
850 \r
851 ################################################################################\r
852 # factory: "docs" — builds documentation with a special asciidoc conf\r
853 ################################################################################\r
854 \r
855 f = factories['docs'] = BuildFactory()\r
856 f.addStep(s_git)\r
857 # Fill the 'gitversion' property with the output of git describe --tags.\r
858 f.addStep(shell.SetProperty(command = 'git describe --tags', property = 'gitversion'))\r
859 cmd(f, name = 'build docs', command = [ 'make', '-C', 'docs', "ASCIIDOC=asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf" ])\r
860 cmd(f, name = 'build manpages', command = "for file in $(sed 's/\.1$/.man/g' debian/i3-wm.manpages); do asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf \"$file\"; done")\r
861 f.addStep(slave.MakeDirectory(dir='build/COPY-DOCS'))\r
862 cmd(f, name = 'copy docs', command = "cp $(tr '\\n' ' ' &lt; debian/i3-wm.docs) COPY-DOCS")\r
863 cmd(f, name = 'copy manpages', command = "cp $(sed 's/\.1$/.html/g' debian/i3-wm.manpages | tr '\\n' ' ') COPY-DOCS")\r
864 \r
865 f.addStep(transfer.DirectoryUpload(\r
866     slavesrc = 'COPY-DOCS',\r
867     masterdest = 'htdocs/docs-git',\r
868     compress = 'bz2',\r
869     name = 'upload docs'))\r
870 \r
871 f.addStep(DocsToIRC())\r
872 \r
873 ################################################################################\r
874 # factory: "debian-packages" — builds Debian (sid) packages for amd64 and i386\r
875 ################################################################################\r
876 \r
877 distributions = [ 'sid-amd64', 'sid-i386' ]\r
878 gpg_key = 'BE1DB1F1'\r
879 \r
880 f = factories['debian-packages'] = BuildFactory()\r
881 # We need the git repository for the Debian packaging.\r
882 f.addStep(s_git)\r
883 unpack_dist_tarball(f)\r
884 cmd(f, name='copy packaging', command = "cp -r debian DIST/")\r
885 \r
886 # Add a new changelog entry to have the git version in the package version.\r
887 cmd(f,\r
888     name = 'update changelog',\r
889     workdir = 'build/DIST',\r
890     command = [ 'debchange', '-m', '-l', WithProperties('+g%(gitversion)s'), 'Automatically built' ],\r
891 )\r
892 \r
893 cmd(f,\r
894     name = 'source pkg',\r
895     command = [ 'dpkg-buildpackage', '-S', '-us', '-uc' ],\r
896     workdir = 'build/DIST',\r
897 )\r
898 \r
899 for dist in distributions:\r
900     f.addStep(slave.MakeDirectory(dir='build/RESULT-' + dist))\r
901 \r
902 # Create debian sid repository\r
903 f.addStep(slave.MakeDirectory(dir='build/REPO-sid/conf'))\r
904 f.addStep(transfer.StringDownload(\r
905     """Codename: sid\r
906 Suite: unstable\r
907 Architectures: i386 amd64 source\r
908 Components: main\r
909 DebIndices: Packages Release . .gz .bz2\r
910 DscIndices: Sources Release . .gz .bz2\r
911 SignWith: %(gpg_key)s\r
912 """ % { "gpg_key": gpg_key },\r
913     slavedest = 'REPO-sid/conf/distributions',\r
914 ))\r
915 \r
916 # add source package to repository\r
917 reprepro_include(f, 'i3-wm*_source.changes', 'dsc')\r
918 \r
919 # Add keyring to the repository. We need to run git clone on our own because\r
920 # the Git() step assumes there’s precisely one repository we want to deal with.\r
921 # No big deal since the i3-autobuild-keyring repository is not big.\r
922 cmd(f, name='clone keyring repo', command = 'git clone git://code.i3wm.org/i3-autobuild-keyring')\r
923 reprepro_include(f, 'i3-autobuild-keyring/prebuilt/*.changes')\r
924 \r
925 for dist in distributions:\r
926     # update the pbuilder\r
927     cmd(f, name = 'update builder', command = 'pbuilder-' + dist + ' update')\r
928 \r
929     # build the package for each dist\r
930     f.addStep(ShellCommand(\r
931         logEnviron = False,\r
932         name = 'pkg ' + dist,\r
933         command = 'pbuilder-' + dist + ' build --binary-arch \\r
934 --buildresult RESULT-' + dist + ' --debbuildopts -j8 i3-wm*dsc',\r
935         warnOnFailure = True\r
936     ))\r
937 \r
938     reprepro_include(f, 'RESULT-' + dist + '/*.changes')\r
939 \r
940 # upload the sid repo\r
941 # Since the next step is cleaning up old files, we set haltOnFailure=True -- we\r
942 # prefer providing old packages over providing no packages at all :).\r
943 for directory in [ 'pool', 'dists' ]:\r
944     f.addStep(transfer.DirectoryUpload(\r
945         slavesrc = 'REPO-sid/' + directory,\r
946         masterdest = 'htdocs/debian/sid/' + directory,\r
947         compress = 'bz2',\r
948         name = 'upload sid ' + directory,\r
949         haltOnFailure = True,\r
950     ))\r
951 \r
952 f.addStep(master.MasterShellCommand(\r
953     command = "find htdocs/debian/sid/pool -mtime +3 -exec rm '{}' \;",\r
954     name = 'cleanup old packages',\r
955 ))\r
956 \r
957 # We ensure there is an empty i18n/Index to speed up apt (so that it does not\r
958 # try to download Translation-*)\r
959 f.addStep(master.MasterShellCommand(\r
960     command = [ 'mkdir', '-p', 'htdocs/debian/sid/dists/sid/main/i18n' ],\r
961     name = 'create i18n folder',\r
962 ))\r
963 f.addStep(master.MasterShellCommand(\r
964     command = [ 'touch', 'htdocs/debian/sid/dists/sid/main/i18n/Index' ],\r
965     name = 'touch i18n/Index',\r
966 ))\r
967 \r
968 ################################################################################\r
969 # factory: "ubuntu-packages" — builds Ubuntu (precise) packages for amd64 and i386\r
970 ################################################################################\r
971 \r
972 distributions = [ 'precise-amd64', 'precise-i386' ]\r
973 gpg_key = 'BE1DB1F1'\r
974 \r
975 f = factories['ubuntu-packages'] = BuildFactory()\r
976 # We need the git repository for the Debian packaging.\r
977 f.addStep(s_git)\r
978 unpack_dist_tarball(f)\r
979 cmd(f, name='copy packaging', command = "cp -r debian DIST/")\r
980 \r
981 # Add a new changelog entry to have the git version in the package version.\r
982 cmd(f,\r
983     name = 'update changelog',\r
984     workdir = 'build/DIST',\r
985     command = [ 'debchange', '-m', '-l', WithProperties('+g%(gitversion)s'), 'Automatically built' ],\r
986 )\r
987 \r
988 cmd(f,\r
989     name = 'source pkg',\r
990     command = [ 'dpkg-buildpackage', '-S', '-us', '-uc' ],\r
991     workdir = 'build/DIST',\r
992 )\r
993 \r
994 for dist in distributions:\r
995     f.addStep(slave.MakeDirectory(dir='build/RESULT-' + dist))\r
996 \r
997 # Create debian sid repository\r
998 f.addStep(slave.MakeDirectory(dir='build/REPO-sid/conf'))\r
999 f.addStep(transfer.StringDownload(\r
1000     """Codename: precise\r
1001 Suite: unstable\r
1002 Architectures: i386 amd64 source\r
1003 Components: main\r
1004 DebIndices: Packages Release . .gz .bz2\r
1005 DscIndices: Sources Release . .gz .bz2\r
1006 SignWith: %(gpg_key)s\r
1007 """ % { "gpg_key": gpg_key },\r
1008     slavedest = 'REPO-sid/conf/distributions',\r
1009 ))\r
1010 \r
1011 # add source package to repository\r
1012 reprepro_include_ubuntu(f, 'i3-wm*_source.changes', 'dsc')\r
1013 \r
1014 # Add keyring to the repository. We need to run git clone on our own because\r
1015 # the Git() step assumes there’s precisely one repository we want to deal with.\r
1016 # No big deal since the i3-autobuild-keyring repository is not big.\r
1017 cmd(f, name='clone keyring repo', command = 'git clone git://code.i3wm.org/i3-autobuild-keyring')\r
1018 reprepro_include_ubuntu(f, 'i3-autobuild-keyring/prebuilt/*.changes')\r
1019 \r
1020 for dist in distributions:\r
1021     # update the pbuilder\r
1022     cmd(f, name = 'update builder', command = 'pbuilder-' + dist + ' update')\r
1023 \r
1024     # build the package for each dist\r
1025     f.addStep(ShellCommand(\r
1026         logEnviron = False,\r
1027         name = 'pkg ' + dist,\r
1028         command = 'pbuilder-' + dist + ' build --binary-arch \\r
1029 --buildresult RESULT-' + dist + ' --debbuildopts -j8 i3-wm*dsc',\r
1030         warnOnFailure = True\r
1031     ))\r
1032 \r
1033     reprepro_include_ubuntu(f, 'RESULT-' + dist + '/*.changes')\r
1034 \r
1035 # upload the sid repo\r
1036 # Since the next step is cleaning up old files, we set haltOnFailure=True -- we\r
1037 # prefer providing old packages over providing no packages at all :).\r
1038 for directory in [ 'pool', 'dists' ]:\r
1039     f.addStep(transfer.DirectoryUpload(\r
1040         slavesrc = 'REPO-sid/' + directory,\r
1041         masterdest = 'htdocs/ubuntu/precise/' + directory,\r
1042         compress = 'bz2',\r
1043         name = 'upload precise ' + directory,\r
1044         haltOnFailure = True,\r
1045     ))\r
1046 \r
1047 f.addStep(master.MasterShellCommand(\r
1048     command = "find htdocs/ubuntu/precise/pool -mtime +3 -exec rm '{}' \;",\r
1049     name = 'cleanup old packages',\r
1050 ))\r
1051 \r
1052 # We ensure there is an empty i18n/Index to speed up apt (so that it does not\r
1053 # try to download Translation-*)\r
1054 f.addStep(master.MasterShellCommand(\r
1055     command = [ 'mkdir', '-p', 'htdocs/ubuntu/precise/dists/sid/main/i18n' ],\r
1056     name = 'create i18n folder',\r
1057 ))\r
1058 f.addStep(master.MasterShellCommand(\r
1059     command = [ 'touch', 'htdocs/ubuntu/precise/dists/sid/main/i18n/Index' ],\r
1060     name = 'touch i18n/Index',\r
1061 ))\r
1062 \r
1063 \r
1064 c['builders'] = []\r
1065 \r
1066 # Add all builders to all buildslaves.\r
1067 for factoryname in factories.keys():\r
1068     c['builders'].append(BuilderConfig(\r
1069         name = factoryname,\r
1070         slavenames=['docsteel-vm'],\r
1071         factory=factories[factoryname],\r
1072     ))\r
1073 \r
1074 \r
1075 ####### STATUS TARGETS\r
1076 \r
1077 c['status'] = []\r
1078 \r
1079 authz_cfg=authz.Authz(\r
1080     gracefulShutdown = False,\r
1081     forceBuild = False,\r
1082     forceAllBuilds = False,\r
1083     pingBuilder = False,\r
1084     stopBuild = False,\r
1085     stopAllBuilds = False,\r
1086     cancelPendingBuild = False,\r
1087 )\r
1088 \r
1089 c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))\r
1090 \r
1091 c['status'].append(buildbot.status.status_push.HttpStatusPush(\r
1092     serverUrl = 'http://localhost:8080/push_buildbot',\r
1093 ))\r
1094 \r
1095 ####### PROJECT IDENTITY\r
1096 \r
1097 c['title'] = 'i3'\r
1098 c['titleURL'] = 'http://i3wm.org/'\r
1099 # Removed so that search engines don’t crawl it\r
1100 c['buildbotURL'] = 'http://localhost/'\r
1101 \r
1102 ####### DB URL\r
1103 \r
1104 c['db'] = {\r
1105     # This specifies what database buildbot uses to store its state.  You can leave\r
1106     # this at its default for all but the largest installations.\r
1107     'db_url' : "sqlite:///state.sqlite",\r
1108 }</tt></pre>\r
1109 </div></div>\r
1110 </div>\r
1111 </div>\r
1112 </div>\r
1113 <div id="footnotes"><hr /></div>\r
1114 <div id="footer" lang="de">\r
1115 © 2009-2011 Michael Stapelberg, <a href="/impress.html">Impressum</a>\r
1116 </div>\r
1117 </body>\r
1118 </html>\r