def default(cls, env): """Return a prototype for the defaults on the new prototype screen.""" from api import TracForgeAdminSystem steps = TracForgeAdminSystem(env).get_project_setup_participants() proto = cls(env, '') for action, info in steps.iteritems(): default = info['provider'].get_setup_action_default(action, env) if default is not None: proto.append((action, default)) env.log.debug('TracForge: %s', proto) proto.sort() return proto
def sort(self): """Do an in-place topological sort of this prototype.""" from api import TracForgeAdminSystem steps = TracForgeAdminSystem(self.env).get_project_setup_participants() all_provides = set() for action, args in self: all_provides |= set(steps[action].get('provides', ())) effective_depends = {} for action, args in self: # All real deps are always used effective_depends.setdefault(action, []).extend(steps[action].get('depends', ())) for tag in steps[action].get('optional_depends', ()): # Any optional dep that is provided by something else is used if tag in all_provides: effective_depends[action].append(tag) old = set([action for action, args in self]) new = [] tags = set() for i in xrange(len(self)): for action in old: self.env.log.debug('TracForge: %s %s %s %s %s', i, action, old, new, tags) if all([tag in tags for tag in effective_depends[action]]): new.append(action) tags |= set(steps[action].get('provides', [])) old.remove(action) break if not old: break if old: raise ValueError('Cant solve') action_map = dict(self) self[:] = [(action, action_map[action]) for action in new]
def apply(self, req, proj): """Run this prototype on a new project. NOTE: If you pass in a project that isn't new, this could explode. Don't do that. """ from api import TracForgeAdminSystem steps = TracForgeAdminSystem(self.env).get_project_setup_participants() db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('DELETE FROM tracforge_project_log WHERE project=%s', (proj.name, )) db.commit() for step in self: action = args = None if isinstance(step, dict): action = step['action'] args = step['args'] else: action, args = step pid = os.fork() if not pid: #o_fd, o_file = mkstemp('tracforge-step', text=True) #e_fd, e_file = mkstemp('tracforge-step', text=True) o_file = TemporaryFile(prefix='tracforge-step', bufsize=0) e_file = TemporaryFile(prefix='tracforge-step', bufsize=0) sys.stdout = o_file sys.stderr = e_file os.dup2(o_file.fileno(), 1) os.dup2(e_file.fileno(), 2) rv = steps[action]['provider'].execute_setup_action( req, proj, action, args) self.env.log.debug('TracForge: %s() => %r', action, rv) o_file.seek(0, 0) o_data = o_file.read() o_file.close() e_file.seek(0, 0) e_data = e_file.read() e_file.close() db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( 'INSERT INTO tracforge_project_log (project, action, args, return, stdout, stderr) VALUES (%s, %s, %s, %s, %s, %s)', (proj.name, action, args, int(rv), o_data, e_data)) db.commit() db.close() os._exit(0) os.waitpid(pid, 0)
def _show_prototype(self, req, path_info, action): """Handler for creating a new prototype.""" add_stylesheet(req, 'tracforge/css/prototypes_new.css') add_script(req, 'tracforge/js/interface/iutil.js') add_script(req, 'tracforge/js/jquery.animatedswap.js') req.hdf['tracforge.prototypes.name'] = path_info.strip('/') if req.method == 'POST': if req.args.get( 'save' ): # Save either a new prototype or a changed existing ones name = req.args.get('name') if action == 'edit': name = path_info.strip('/') if not name: raise TracError( 'You must specify a name for the prototype') if name in ('new', 'configset'): raise TracError('"new" and "configset" are reserved names') data = req.args.get('data') if data is None: raise TracError( "Warning: Peguins on fire. You might have JavaScript off, don't do that" ) data = data[4:] # Strip off the 'data' literal at the start if not data: raise TracError( "You must have at least one step in a prototype") proto = Prototype(self.env, name) if action == 'new' and proto.exists: raise TracError("Prototype %s already exists" % name) del proto[:] for x in data.split('|'): proto.append(x.split(',', 1)) proto.save() elif req.args.get('cancel'): pass # This should just redirect back elif req.args.get( 'delete' ) and action == 'edit': # Show the confirmation screen return 'admin_tracforge_prototypes_delete.cs', None elif req.args.get( 'reallydelete' ) and action == 'edit': # Actually delete this prototype name = path_info.strip('/') proto = Prototype(self.env, name) if not proto.exists: raise TracError('Prototype %s does not exist' % name) proto.delete() req.redirect(req.href.admin(req.cat, req.page)) #steps = {} #for p in self.setup_participants: # for a in p.get_setup_actions(): # steps[a] = { # 'provider': p, # 'description': p.get_setup_action_description(a), # } steps = TracForgeAdminSystem(self.env).get_project_setup_participants() initial_steps = [] if action == 'new': # For a new one, use the specified defaults initial_steps = Prototype.default( self.env) # XXX: This should really read from trac.ini somehow elif action == 'edit': proto = Prototype(self.env, path_info.strip('/')) if not proto.exists: raise TracError('Unknown prototype %s' % proto.tag) initial_steps = proto else: raise TracError('Invalid action %s' % action) req.hdf['tracforge.prototypes.action'] = action req.hdf['tracforge.prototypes.steps'] = steps req.hdf['tracforge.prototypes.initialsteps'] = initial_steps req.hdf['tracforge.prototypes.liststeps'] = [ k for k in steps.iterkeys() if k not in initial_steps ] return 'admin_tracforge_prototypes_show.cs', None
def _show_prototype(self, req, path_info, action): """Handler for creating a new prototype.""" add_stylesheet(req, 'tracforge/css/prototypes_new.css') add_script(req, 'tracforge/js/interface/iutil.js') add_script(req, 'tracforge/js/jquery.animatedswap.js') req.hdf['tracforge.prototypes.name'] = path_info.strip('/') if req.method == 'POST': if req.args.get('save'): # Save either a new prototype or a changed existing ones name = req.args.get('name') if action == 'edit': name = path_info.strip('/') if not name: raise TracError('You must specify a name for the prototype') if name in ('new', 'configset'): raise TracError('"new" and "configset" are reserved names') data = req.args.get('data') if data is None: raise TracError("Warning: Peguins on fire. You might have JavaScript off, don't do that") data = data[4:] # Strip off the 'data' literal at the start if not data: raise TracError("You must have at least one step in a prototype") proto = Prototype(self.env, name) if action == 'new' and proto.exists: raise TracError("Prototype %s already exists"%name) del proto[:] for x in data.split('|'): proto.append(x.split(',',1)) proto.save() elif req.args.get('cancel'): pass # This should just redirect back elif req.args.get('delete') and action == 'edit': # Show the confirmation screen return 'admin_tracforge_prototypes_delete.cs', None elif req.args.get('reallydelete') and action == 'edit': # Actually delete this prototype name = path_info.strip('/') proto = Prototype(self.env, name) if not proto.exists: raise TracError('Prototype %s does not exist'%name) proto.delete() req.redirect(req.href.admin(req.cat, req.page)) #steps = {} #for p in self.setup_participants: # for a in p.get_setup_actions(): # steps[a] = { # 'provider': p, # 'description': p.get_setup_action_description(a), # } steps = TracForgeAdminSystem(self.env).get_project_setup_participants() initial_steps = [] if action == 'new': # For a new one, use the specified defaults initial_steps = Prototype.default(self.env) # XXX: This should really read from trac.ini somehow elif action == 'edit': proto = Prototype(self.env, path_info.strip('/')) if not proto.exists: raise TracError('Unknown prototype %s'%proto.tag) initial_steps = proto else: raise TracError('Invalid action %s'%action) req.hdf['tracforge.prototypes.action'] = action req.hdf['tracforge.prototypes.steps'] = steps req.hdf['tracforge.prototypes.initialsteps'] = initial_steps req.hdf['tracforge.prototypes.liststeps'] = [k for k in steps.iterkeys() if k not in initial_steps] return 'admin_tracforge_prototypes_show.cs', None
def execute(self, data, direction='execute', project=None): """Run this prototype on a new project. NOTE: If you pass in a project that isn't new, this could explode. Don't do that. """ from api import TracForgeAdminSystem steps = TracForgeAdminSystem(self.env).get_project_setup_participants() # Store this for later orig_direction = direction # Clear out the last attempt at making this project, if any db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('DELETE FROM tracforge_project_log WHERE project=%s AND direction=%s', (data['name'], direction)) cursor.execute('DELETE FROM tracforge_project_output WHERE project=%s AND direction=%s', (data['name'], direction)) db.commit() # Grab the current stdout/err old_stdout = sys.stdout old_stderr = sys.stderr if direction == 'execute': run_buffer = [(action, args, 'execute') for action, args in self] else: cursor.execute('SELECT action, args WHERE project=%s AND direction=%s AND undone=%s ORDER BY step DESC', (project, direction, 0)) run_buffer = [(action, args, 'undo') for action, args in cursor] for i, (action, args, step_direction) in enumerate(run_buffer): #print data['name'], orig_direction, action, step_direction cursor.execute('INSERT INTO tracforge_project_log (project, step, direction, action, step_direction, args, undone) VALUES (%s, %s, %s, %s, %s, %s, %s)', (data['name'], i, orig_direction, action, step_direction, args, 0)) db.commit() def log_cb(stdout, stderr): now = time.time() #print '!'1, stdout, '!', stderr values = [] if stdout: values.append((now, data['name'], orig_direction, action, 'stdout', step_direction, stdout)) if stderr: values.append((now, data['name'], orig_direction, action, 'stderr', step_direction, stderr)) if values: cursor.executemany('INSERT INTO tracforge_project_output ' \ '(ts, project, direction, action, stream, step_direction, data) VALUES ' \ '(%s, %s, %s, %s, %s, %s, %s)', values) db.commit() if getattr(steps[action]['provider'], 'capture_output', True): sys.stdout = _CaptureOutput(cursor, data['name'], orig_direction, action, 'stdout', step_direction) sys.stderr = _CaptureOutput(cursor, data['name'], orig_direction, action, 'stderr', step_direction) try: rv = getattr(steps[action]['provider'], step_direction+'_setup_action')(action, args, data, log_cb) except Exception, e: log_cb('', traceback.format_exc()) rv = False cursor.execute('UPDATE tracforge_project_log SET return=%s WHERE project=%s AND direction=%s AND action=%s AND step_direction=%s', (int(rv), data['name'], orig_direction, action, step_direction)) db.commit() if not rv and direction == 'execute': # Failure, initiate rollback direction = 'undo' del run_buffer[i+1:] run_buffer.extend([(action, args, 'undo') for action, args, _ in reversed(run_buffer)])