changeset 184:fbf949f4c706

Allow invalidation of builds from the web interface. This results in the build being reset to ''PENDING'' status, and all build logs, slave information and reports deleted. Basically initiates a rebuild for a specific revision and target platform.
author cmlenz
date Wed, 31 Aug 2005 16:43:05 +0000
parents fda952491beb
children 2c24d9a950ed
files bitten/master.py bitten/store.py bitten/trac_ext/charts.py bitten/trac_ext/templates/bitten_build.cs bitten/trac_ext/templates/bitten_config.cs bitten/trac_ext/web_ui.py
diffstat 6 files changed, 68 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/bitten/master.py
+++ b/bitten/master.py
@@ -109,6 +109,7 @@
     def _cleanup_orphaned_builds(self):
         # Reset all in-progress builds
         db = self.env.get_db_cnx()
+        store = ReportStore(self.env)
         for build in Build.select(self.env, status=Build.IN_PROGRESS, db=db):
             build.status = Build.PENDING
             build.slave = None
@@ -119,6 +120,7 @@
             for build_log in BuildLog.select(self.env, build=build.id):
                 build_log.delete(db=db)
             build.update(db=db)
+            store.delete_reports(build=build)
         db.commit()
 
     def _cleanup_snapshots(self, when):
@@ -188,8 +190,7 @@
                      build.config, handler.name)
             for step in BuildStep.select(self.env, build=build.id):
                 step.delete(db=db)
-            for build_log in BuildLog.select(self.env, build=build.id):
-                build_log.delete(db=db)
+
             build.slave = None
             build.slave_info = {}
             build.status = Build.PENDING
@@ -386,12 +387,17 @@
     def _build_aborted(self, db, build):
         log.info('Slave "%s" aborted build %d ("%s" as of [%s])',
                  self.name, build.id, build.config, build.rev)
+
+        for step in BuildStep.select(self.env, build=build.id, db=db):
+            step.delete(db=db)
+
+        store = ReportStore(self.env)
+        store.delete_reports(build=build.id)
+
         build.slave = None
         build.started = 0
         build.status = Build.PENDING
         build.slave_info = {}
-        for step in BuildStep.select(self.env, build=build.id, db=db):
-            step.delete(db=db)
 
 
 def _parse_iso_datetime(string):
--- a/bitten/store.py
+++ b/bitten/store.py
@@ -29,12 +29,9 @@
 
     backends = ExtensionPoint(IReportStoreBackend)
 
-    def store_report(self, build, step, xml):
-        assert xml.name == 'report' and 'type' in xml.attr
+    def delete_reports(self, config=None, build=None, step=None, type=None):
         backend = self._get_configured_backend()
-        log.debug('Storing report of type "%s" in %s', xml.attr['type'],
-                  backend.__class__.__name__)
-        backend.store_report(build, step, xml)
+        return backend.delete_reports(config, build, step, type)
 
     def query_reports(self, xquery, config=None, build=None, step=None,
                      type=None):
@@ -45,6 +42,13 @@
         backend = self._get_configured_backend()
         return backend.retrieve_reports(build, step, type)
 
+    def store_report(self, build, step, xml):
+        assert xml.name == 'report' and 'type' in xml.attr
+        backend = self._get_configured_backend()
+        log.debug('Storing report of type "%s" in %s', xml.attr['type'],
+                  backend.__class__.__name__)
+        backend.store_report(build, step, xml)
+
     def _get_configured_backend(self):
         configured = self.config.get('bitten', 'report_store', 'BDBXMLBackend')
         for backend in self.backends:
@@ -110,6 +114,17 @@
     def __init__(self):
         self.path = os.path.join(self.env.path, 'db', 'bitten.dbxml')
 
+    def delete_reports(self, config=None, build=None, step=None, type=None):
+        if dbxml is None:
+            log.warning('BDB XML not installed, cannot store report')
+            return
+        mgr = dbxml.XmlManager()
+        container = self._open_container(mgr, create=True)
+        ctxt = mgr.createUpdateContext()
+        for elem in self.query_reports('return $reports', config=config,
+                                       build=build, step=step, type=type):
+            container.deleteDocument(elem._value.asDocument(), ctxt)
+
     def store_report(self, build, step, xml):
         if dbxml is None:
             log.warning('BDB XML not installed, cannot store report')
--- a/bitten/trac_ext/charts.py
+++ b/bitten/trac_ext/charts.py
@@ -59,6 +59,8 @@
         rev_time = {}
         rev = {}
         for build in Build.select(self.env, config=config.name):
+            if build.status in (Build.PENDING, Build.IN_PROGRESS):
+                continue
             rev[str(build.id)] = build.rev
             rev_time[str(build.id)] = datetime.fromtimestamp(build.rev_time)
 
--- a/bitten/trac_ext/templates/bitten_build.cs
+++ b/bitten/trac_ext/templates/bitten_build.cs
@@ -14,6 +14,14 @@
     /if ?>)</p>
   <p class="time">Completed: <?cs var:build.started ?> (<?cs
     var:build.started_delta ?> ago)<br />Took: <?cs var:build.duration ?></p><?cs
+  if:build.can_delete ?>
+   <div class="buttons">
+    <form method="post" action=""><div>
+     <input type="hidden" name="action" value="invalidate" />
+     <input type="submit" value="Invalidate build" />
+    </div></form>
+   </div><?cs
+  /if ?><?cs
   each:step = build.steps ?>
    <h2 id="<?cs var:step.name ?>"><?cs var:step.name ?> (<?cs
      var:step.duration ?>)</h2>
--- a/bitten/trac_ext/templates/bitten_config.cs
+++ b/bitten/trac_ext/templates/bitten_config.cs
@@ -99,7 +99,6 @@
    if:config.description ?><div class="description"><?cs
      var:config.description ?></div><?cs
    /if ?>
-   
    <div id="charts"><?cs
     each:chart = config.charts ?>
      <object type="application/x-shockwave-flash" width="320" height="240" data="<?cs
@@ -112,9 +111,8 @@
       <param name="wmode" value="transparent" />
      </object><br /><?cs
     /each ?>
-   </div>
-   
-   <?cs
+   </div><?cs
+
    if:config.can_modify ?>
     <div class="buttons">
      <form method="get" action=""><div>
--- a/bitten/trac_ext/web_ui.py
+++ b/bitten/trac_ext/web_ui.py
@@ -84,7 +84,7 @@
     # IRequestHandler methods
 
     def match_request(self, req):
-        match = re.match(r'/build(?:/([\w.-]+))?$', req.path_info)
+        match = re.match(r'/build(?:/([\w.-]+))?/?$', req.path_info)
         if match:
             if match.group(1):
                 req.args['config'] = match.group(1)
@@ -417,6 +417,11 @@
         build = Build.fetch(self.env, build_id, db=db)
         assert build, 'Build %s does not exist' % build_id
 
+        if req.method == 'POST':
+            if req.args.get('action') == 'invalidate':
+                self._do_invalidate(req, build, db)
+            req.redirect(self.env.href.build(build.config, build.id))
+
         add_link(req, 'up', self.env.href.build(build.config),
                  'Build Configuration')
         status2title = {Build.SUCCESS: 'Success', Build.FAILURE: 'Failure',
@@ -441,6 +446,7 @@
                 'reports': self._render_reports(req, build, step)
             })
         req.hdf['build.steps'] = steps
+        req.hdf['build.can_delete'] = req.perm.has_permission('BUILD_DELETE')
 
         add_stylesheet(req, 'bitten/bitten.css')
         return 'bitten_build.cs', None
@@ -477,6 +483,25 @@
 
     # Internal methods
 
+    def _do_invalidate(self, req, build, db):
+        self.log.info('Invalidating build %d', build.id)
+
+        for step in BuildStep.select(self.env, build=build.id, db=db):
+            step.delete(db=db)
+
+        store = ReportStore(self.env)
+        store.delete_reports(build=build)
+
+        build.slave = None
+        build.started = build.stopped = 0
+        build.status = Build.PENDING
+        build.slave_info = {}
+        build.update()
+
+        db.commit()
+
+        req.redirect(self.env.href.build(build.config))
+
     def _render_log(self, req, build, step):
         items = []
         for log in BuildLog.select(self.env, build=build.id, step=step.name):
Copyright (C) 2012-2017 Edgewall Software