web-refactoring: finally got the Waterfall rendering again, lots of bad links and...
[buildbot.git] / buildbot / status / web / site.py
blobe30e722323a93e0c1250ea854556326047c13532
2 class StatusResource(Resource):
3 status = None
4 control = None
5 favicon = None
6 robots_txt = None
8 def __init__(self, status, control, changemaster, categories, css):
9 """
10 @type status: L{buildbot.status.builder.Status}
11 @type control: L{buildbot.master.Control}
12 @type changemaster: L{buildbot.changes.changes.ChangeMaster}
13 """
14 Resource.__init__(self)
15 self.status = status
16 self.control = control
17 self.changemaster = changemaster
18 self.css = css
19 waterfall = WaterfallStatusResource(categories, css)
20 waterfall.status = self.status
21 waterfall.control = control
22 waterfall.changemaster = changemaster
23 self.putChild("", waterfall)
25 def render(self, request):
26 request.redirect(request.prePathURL() + '/')
27 request.finish()
29 def getChild(self, path, request):
30 if path == "robots.txt" and self.robots_txt:
31 return static.File(self.robots_txt)
32 if path == "buildbot.css" and self.css:
33 return static.File(self.css)
34 if path == "changes":
35 return StatusResourceChanges(self.status, self.changemaster)
36 if path == "favicon.ico":
37 if self.favicon:
38 return static.File(self.favicon)
39 return NoResource("No favicon.ico registered")
41 if path in self.status.getBuilderNames():
42 builder = self.status.getBuilder(path)
43 control = None
44 if self.control:
45 control = self.control.getBuilder(path)
46 return StatusResourceBuilder(self.status, builder, control)
48 return NoResource("No such Builder '%s'" % path)
50 if hasattr(sys, "frozen"):
51 # all 'data' files are in the directory of our executable
52 here = os.path.dirname(sys.executable)
53 buildbot_icon = os.path.abspath(os.path.join(here, "buildbot.png"))
54 buildbot_css = os.path.abspath(os.path.join(here, "classic.css"))
55 else:
56 # running from source
57 # the icon is sibpath(__file__, "../buildbot.png") . This is for
58 # portability.
59 up = os.path.dirname
60 buildbot_icon = os.path.abspath(os.path.join(up(up(up(__file__))),
61 "buildbot.png"))
62 buildbot_css = os.path.abspath(os.path.join(up(__file__), "classic.css"))
64 class Waterfall(base.StatusReceiverMultiService):
65 """I implement the primary web-page status interface, called a 'Waterfall
66 Display' because builds and steps are presented in a grid of boxes which
67 move downwards over time. The top edge is always the present. Each column
68 represents a single builder. Each box describes a single Step, which may
69 have logfiles or other status information.
71 All these pages are served via a web server of some sort. The simplest
72 approach is to let the buildmaster run its own webserver, on a given TCP
73 port, but it can also publish its pages to a L{twisted.web.distrib}
74 distributed web server (which lets the buildbot pages be a subset of some
75 other web server).
77 Since 0.6.3, BuildBot defines class attributes on elements so they can be
78 styled with CSS stylesheets. Buildbot uses some generic classes to
79 identify the type of object, and some more specific classes for the
80 various kinds of those types. It does this by specifying both in the
81 class attributes where applicable, separated by a space. It is important
82 that in your CSS you declare the more generic class styles above the more
83 specific ones. For example, first define a style for .Event, and below
84 that for .SUCCESS
86 The following CSS class names are used:
87 - Activity, Event, BuildStep, LastBuild: general classes
88 - waiting, interlocked, building, offline, idle: Activity states
89 - start, running, success, failure, warnings, skipped, exception:
90 LastBuild and BuildStep states
91 - Change: box with change
92 - Builder: box for builder name (at top)
93 - Project
94 - Time
96 @type parent: L{buildbot.master.BuildMaster}
97 @ivar parent: like all status plugins, this object is a child of the
98 BuildMaster, so C{.parent} points to a
99 L{buildbot.master.BuildMaster} instance, through which
100 the status-reporting object is acquired.
103 compare_attrs = ["http_port", "distrib_port", "allowForce",
104 "categories", "css", "favicon", "robots_txt"]
106 def __init__(self, http_port=None, distrib_port=None, allowForce=True,
107 categories=None, css=buildbot_css, favicon=buildbot_icon,
108 robots_txt=None):
109 """To have the buildbot run its own web server, pass a port number to
110 C{http_port}. To have it run a web.distrib server
112 @type http_port: int or L{twisted.application.strports} string
113 @param http_port: a strports specification describing which port the
114 buildbot should use for its web server, with the
115 Waterfall display as the root page. For backwards
116 compatibility this can also be an int. Use
117 'tcp:8000' to listen on that port, or
118 'tcp:12345:interface=127.0.0.1' if you only want
119 local processes to connect to it (perhaps because
120 you are using an HTTP reverse proxy to make the
121 buildbot available to the outside world, and do not
122 want to make the raw port visible).
124 @type distrib_port: int or L{twisted.application.strports} string
125 @param distrib_port: Use this if you want to publish the Waterfall
126 page using web.distrib instead. The most common
127 case is to provide a string that is an absolute
128 pathname to the unix socket on which the
129 publisher should listen
130 (C{os.path.expanduser(~/.twistd-web-pb)} will
131 match the default settings of a standard
132 twisted.web 'personal web server'). Another
133 possibility is to pass an integer, which means
134 the publisher should listen on a TCP socket,
135 allowing the web server to be on a different
136 machine entirely. Both forms are provided for
137 backwards compatibility; the preferred form is a
138 strports specification like
139 'unix:/home/buildbot/.twistd-web-pb'. Providing
140 a non-absolute pathname will probably confuse
141 the strports parser.
143 @type allowForce: bool
144 @param allowForce: if True, present a 'Force Build' button on the
145 per-Builder page that allows visitors to the web
146 site to initiate a build. If False, don't provide
147 this button.
149 @type favicon: string
150 @param favicon: if set, provide the pathname of an image file that
151 will be used for the 'favicon.ico' resource. Many
152 browsers automatically request this file and use it
153 as an icon in any bookmark generated from this site.
154 Defaults to the buildbot/buildbot.png image provided
155 in the distribution. Can be set to None to avoid
156 using a favicon at all.
158 @type robots_txt: string
159 @param robots_txt: if set, provide the pathname of a robots.txt file.
160 Many search engines request this file and obey the
161 rules in it. E.g. to disallow them to crawl the
162 status page, put the following two lines in
163 robots.txt::
164 User-agent: *
165 Disallow: /
168 base.StatusReceiverMultiService.__init__(self)
169 assert allowForce in (True, False) # TODO: implement others
170 if type(http_port) is int:
171 http_port = "tcp:%d" % http_port
172 self.http_port = http_port
173 if distrib_port is not None:
174 if type(distrib_port) is int:
175 distrib_port = "tcp:%d" % distrib_port
176 if distrib_port[0] in "/~.": # pathnames
177 distrib_port = "unix:%s" % distrib_port
178 self.distrib_port = distrib_port
179 self.allowForce = allowForce
180 self.categories = categories
181 self.css = css
182 self.favicon = favicon
183 self.robots_txt = robots_txt
185 def __repr__(self):
186 if self.http_port is None:
187 return "<Waterfall on path %s>" % self.distrib_port
188 if self.distrib_port is None:
189 return "<Waterfall on port %s>" % self.http_port
190 return "<Waterfall on port %s and path %s>" % (self.http_port,
191 self.distrib_port)
193 def setServiceParent(self, parent):
195 @type parent: L{buildbot.master.BuildMaster}
197 base.StatusReceiverMultiService.setServiceParent(self, parent)
198 self.setup()
200 def setup(self):
201 status = self.parent.getStatus()
202 if self.allowForce:
203 control = interfaces.IControl(self.parent)
204 else:
205 control = None
206 change_svc = self.parent.change_svc
207 sr = StatusResource(status, control, change_svc, self.categories,
208 self.css)
209 sr.favicon = self.favicon
210 sr.robots_txt = self.robots_txt
211 self.site = server.Site(sr)
213 if self.http_port is not None:
214 s = strports.service(self.http_port, self.site)
215 s.setServiceParent(self)
216 if self.distrib_port is not None:
217 f = pb.PBServerFactory(distrib.ResourcePublisher(self.site))
218 s = strports.service(self.distrib_port, f)
219 s.setServiceParent(self)