def test_populate_not_build_all(self): self.env.get_repository = lambda authname=None: Mock( get_changeset=lambda rev: Mock(date=to_datetime(rev * 1000, utc)), get_node=lambda path, rev=None: Mock( get_entries=lambda: [Mock(), Mock()], get_history=lambda: [('somepath', 123, 'edit'), ('somepath', 121, 'edit'), ('somepath', 120, 'edit')] ), normalize_path=lambda path: path, rev_older_than=lambda rev1, rev2: rev1 < rev2 ) BuildConfig(self.env, 'test', path='somepath', active=True).insert() platform1 = TargetPlatform(self.env, config='test', name='P1') platform1.insert() platform2 = TargetPlatform(self.env, config='test', name='P2') platform2.insert() queue = BuildQueue(self.env) queue.populate() queue.populate() queue.populate() builds = list(Build.select(self.env, config='test')) builds.sort(lambda a, b: cmp(a.platform, b.platform)) self.assertEqual(2, len(builds)) self.assertEqual(platform1.id, builds[0].platform) self.assertEqual('123', builds[0].rev) self.assertEqual(platform2.id, builds[1].platform) self.assertEqual('123', builds[1].rev)
def should_delete_build(self, build, repos): config = BuildConfig.fetch(self.env, build.config) platform = TargetPlatform.fetch(self.env, build.platform) # Platform may or may not exist anymore - get safe name for logging platform_name = platform and platform.name \ or 'unknown platform "%s"' % build.platform # Drop build if platform no longer exists if not platform: self.log.info( 'Dropping build of configuration "%s" at ' 'revision [%s] on %s because the platform no longer ' 'exists', config.name, build.rev, platform_name) return True # Ignore pending builds for deactived build configs if not config.active: self.log.info( 'Dropping build of configuration "%s" at ' 'revision [%s] on %s because the configuration is ' 'deactivated', config.name, build.rev, platform_name) return True # If not 'build_all', drop if a more recent revision is available if not self.build_all and \ len(list(Build.select(self.env, config=build.config, min_rev_time=build.rev_time, platform=build.platform))) > 1: self.log.info( 'Dropping build of configuration "%s" at revision [%s] ' 'on "%s" because a more recent build exists', config.name, build.rev, platform_name) return True return False
def get_annotation_data(self, context): add_stylesheet(context.req, 'bitten/bitten_coverage.css') resource = context.resource self.log.debug("Looking for coverage report for %s@%s..." % ( resource.id, str(resource.version))) builds = Build.select(self.env, rev=resource.version) reports = [] for build in builds: config = BuildConfig.fetch(self.env, build.config) if not resource.id.startswith(config.branch): continue reports = Report.select(self.env, build=build.id, category='coverage') branch_in_config = resource.id[len(config.branch):] for report in reports: for item in report.items: if item.get('file') == branch_in_config: coverage = item.get('line_hits', '').split() if coverage: # Return first result with line data self.log.debug( "Coverage annotate for %s@%s: %s" % \ (resource.id, resource.version, coverage)) return coverage return []
def reset_orphaned_builds(self): """Reset all in-progress builds to ``PENDING`` state if they've been running so long that the configured timeout has been reached. This is used to cleanup after slaves that have unexpectedly cancelled a build without notifying the master, or are for some other reason not reporting back status updates. """ if not self.timeout: # If no timeout is set, none of the in-progress builds can be # considered orphaned return db = self.env.get_db_cnx() now = int(time.time()) for build in Build.select(self.env, status=Build.IN_PROGRESS, db=db): if now - build.started < self.timeout: # This build has not reached the timeout yet, assume it's still # being executed # FIXME: ideally, we'd base this check on the last activity on # the build, not the start time continue build.status = Build.PENDING build.slave = None build.slave_info = {} build.started = 0 for step in list(BuildStep.select(self.env, build=build.id, db=db)): step.delete(db=db) build.update(db=db) db.commit()
def should_delete_build(self, build, repos): config = BuildConfig.fetch(self.env, build.config) platform = TargetPlatform.fetch(self.env, build.platform) # Platform may or may not exist anymore - get safe name for logging platform_name = platform and platform.name \ or 'unknown platform "%s"' % build.platform # Drop build if platform no longer exists if not platform: self.log.info('Dropping build of configuration "%s" at ' 'revision [%s] on %s because the platform no longer ' 'exists', config.name, build.rev, platform_name) return True # Ignore pending builds for deactived build configs if not config.active: self.log.info('Dropping build of configuration "%s" at ' 'revision [%s] on %s because the configuration is ' 'deactivated', config.name, build.rev, platform_name) return True # If not 'build_all', drop if a more recent revision is available if not self.build_all and \ len(list(Build.select(self.env, config=build.config, min_rev_time=build.rev_time, platform=build.platform))) > 1: self.log.info('Dropping build of configuration "%s" at revision [%s] ' 'on "%s" because a more recent build exists', config.name, build.rev, platform_name) return True return False
def getBuilds(self, req, config=None, rev=None, platform=None, slave=None, status=None): """Retrieve a list of builds that match the selected criteria, with all build properties """ builds = [] for build in Build.select(self.env, config=config or None, rev=rev or None, platform=platform or None, slave=slave or None, status=status or None): if not build: continue builds.append({ 'id': build.id, 'config': build.config, 'rev': build.rev, 'platform': build.platform, 'slave': build.slave, 'started': build.started, 'stopped': build.stopped, 'last_activity': build.last_activity, 'rev_time': build.rev_time, 'status': build.status, }) return builds
def get_annotation_data(self, context): """add annotation data for lint""" context.perm.require('BUILD_VIEW') add_stylesheet(context.req, 'bitten/bitten_coverage.css') add_stylesheet(context.req, 'bitten/bitten_lintannotator.css') resource = context.resource # attempt to use the version passed in with the request, # otherwise fall back to the latest version of this file. try: version = context.req.args['rev'] except (KeyError, TypeError): version = resource.version self.log.debug('no version passed to get_annotation_data') builds = Build.select(self.env, rev=version) self.log.debug("Looking for lint report for %s@%s [%s]..." % (resource.id, str(resource.version), version)) self.itemid = 0 data = {} reports = None for build in builds: config = BuildConfig.fetch(self.env, build.config) if not resource.id.lstrip('/').startswith(config.path.lstrip('/')): self.log.debug('Skip build %s' % build) continue path_in_config = resource.id[len(config.path) + 1:].lstrip('/') reports = Report.select(self.env, build=build.id, category='lint') for report in reports: for item in report.items: if item.get('file') == path_in_config: line = item.get('line') if line: problem = { 'category': item.get('category', ''), 'tag': item.get('tag', ''), 'bid': build.id, 'rbuild': report.build, 'rstep': report.step, 'rid': report.id } data.setdefault(int(line), []).append(problem) if data: self.log.debug("Lint annotate for %s@%s: %s results" % \ (resource.id, resource.version, len(data))) return data if not builds: self.log.debug("No builds found") elif not reports: self.log.debug("No reports found") else: self.log.debug("No item of any report matched (%s)" % reports) return None
def get_annotation_data(self, context): """add annotation data for lint""" context.perm.require('BUILD_VIEW') add_stylesheet(context.req, 'bitten/bitten_coverage.css') add_stylesheet(context.req, 'bitten/bitten_lintannotator.css') resource = context.resource # attempt to use the version passed in with the request, # otherwise fall back to the latest version of this file. try: version = context.req.args['rev'] except (KeyError, TypeError): version = resource.version self.log.debug('no version passed to get_annotation_data') builds = Build.select(self.env, rev=version) self.log.debug("Looking for lint report for %s@%s [%s]..." % ( resource.id, str(resource.version), version)) self.itemid = 0 data = {} reports = None for build in builds: config = BuildConfig.fetch(self.env, build.config) if not resource.id.lstrip('/').startswith(config.path.lstrip('/')): self.log.debug('Skip build %s' % build) continue path_in_config = resource.id[len(config.path)+1:].lstrip('/') reports = Report.select(self.env, build=build.id, category='lint') for report in reports: for item in report.items: if item.get('file') == path_in_config: line = item.get('line') if line: problem = {'category': item.get('category', ''), 'tag': item.get('tag', ''), 'bid': build.id, 'rbuild': report.build, 'rstep': report.step, 'rid': report.id} data.setdefault(int(line), []).append(problem) if data: self.log.debug("Lint annotate for %s@%s: %s results" % \ (resource.id, resource.version, len(data))) return data if not builds: self.log.debug("No builds found") elif not reports: self.log.debug("No reports found") else: self.log.debug("No item of any report matched (%s)" % reports) return None
def test_populate_thread_race_condition(self): messages = [] self.env.log = Mock(info=lambda msg, *args: messages.append(msg)) def get_history(): yield ('somepath', 123, 'edit') yield ('somepath', 121, 'edit') yield ('somepath', 120, 'edit') time.sleep(1) # sleep to make sure both threads collect self.env.get_repository = lambda authname=None: Mock( get_changeset=lambda rev: Mock(date=to_datetime(rev * 1000, utc)), get_node=lambda path, rev=None: Mock( get_entries=lambda: [Mock(), Mock()], get_history=get_history), normalize_path=lambda path: path, rev_older_than=lambda rev1, rev2: rev1 < rev2) BuildConfig(self.env, 'test', path='somepath', active=True).insert() platform1 = TargetPlatform(self.env, config='test', name='P1') platform1.insert() platform2 = TargetPlatform(self.env, config='test', name='P2') platform2.insert() def build_populator(): queue = BuildQueue(self.env, build_all=True) queue.populate() thread1 = threading.Thread(target=build_populator) thread2 = threading.Thread(target=build_populator) thread1.start() thread2.start() thread1.join() thread2.join() # check builds got added builds = list(Build.select(self.env, config='test')) builds.sort(lambda a, b: cmp(a.platform, b.platform)) self.assertEqual(6, len(builds)) self.assertEqual(platform1.id, builds[0].platform) self.assertEqual('123', builds[0].rev) self.assertEqual(platform1.id, builds[1].platform) self.assertEqual('121', builds[1].rev) self.assertEqual(platform1.id, builds[2].platform) self.assertEqual('120', builds[2].rev) self.assertEqual(platform2.id, builds[3].platform) self.assertEqual('123', builds[3].rev) self.assertEqual(platform2.id, builds[4].platform) self.assertEqual('121', builds[4].rev) self.assertEqual(platform2.id, builds[5].platform) self.assertEqual('120', builds[5].rev) # check attempts at duplicate inserts were logged. failure_messages = [ x for x in messages if x.startswith('Failed to insert build') ] self.assertEqual(6, len(failure_messages))
def test_delete_platform_with_pending_builds(self): """Check that deleting a platform with pending builds removes those pending builds""" db = self.env.get_db_cnx() platform = TargetPlatform(self.env, config='test', name='Linux') platform.insert() build = Build(self.env, config='test', platform=platform.id, rev='42', rev_time=12039) build.insert() platform.delete() pending = list(build.select(self.env, config='test', status=Build.PENDING)) self.assertEqual(0, len(pending))
def test_populate_thread_race_condition(self): messages = [] self.env.log = Mock(info=lambda msg, *args: messages.append(msg)) def get_history(): yield ('somepath', 123, 'edit') yield ('somepath', 121, 'edit') yield ('somepath', 120, 'edit') time.sleep(1) # sleep to make sure both threads collect self.repos.get_changeset=lambda rev: Mock( date=to_datetime(rev * 1000, utc)) self.repos.get_node=lambda path, rev=None: Mock( get_entries=lambda: [Mock(), Mock()], get_history=get_history) self.repos.normalize_path=lambda path: path self.repos.rev_older_than=lambda rev1, rev2: rev1 < rev2 BuildConfig(self.env, 'test', path='somepath', active=True).insert() platform1 = TargetPlatform(self.env, config='test', name='P1') platform1.insert() platform2 = TargetPlatform(self.env, config='test', name='P2') platform2.insert() def build_populator(): queue = BuildQueue(self.env, build_all=True) queue.populate() thread1 = threading.Thread(target=build_populator) thread2 = threading.Thread(target=build_populator) # tiny sleep is to avoid odd segementation faults # (on Linux) and bus errors (on Mac OS X) thread1.start(); time.sleep(0.01); thread2.start() thread1.join(); thread2.join() # check builds got added builds = list(Build.select(self.env, config='test')) builds.sort(lambda a, b: cmp(a.platform, b.platform)) self.assertEqual(6, len(builds)) self.assertEqual(platform1.id, builds[0].platform) self.assertEqual('123', builds[0].rev) self.assertEqual(platform1.id, builds[1].platform) self.assertEqual('121', builds[1].rev) self.assertEqual(platform1.id, builds[2].platform) self.assertEqual('120', builds[2].rev) self.assertEqual(platform2.id, builds[3].platform) self.assertEqual('123', builds[3].rev) self.assertEqual(platform2.id, builds[4].platform) self.assertEqual('121', builds[4].rev) self.assertEqual(platform2.id, builds[5].platform) self.assertEqual('120', builds[5].rev) # check attempts at duplicate inserts were logged. failure_messages = [x for x in messages if x.startswith('Failed to insert build')] self.assertEqual(6, len(failure_messages))
def get_build_for_slave(self, name, properties): """Check whether one of the pending builds can be built by the build slave. :param name: the name of the slave :type name: `basestring` :param properties: the slave configuration :type properties: `dict` :return: the allocated build, or `None` if no build was found :rtype: `Build` """ self.log.debug('Checking for pending builds...') db = self.env.get_db_cnx() repos = self.env.get_repository() assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' self.reset_orphaned_builds() # Iterate through pending builds by descending revision timestamp, to # avoid the first configuration/platform getting all the builds platforms = [p.id for p in self.match_slave(name, properties)] builds_to_delete = [] build_found = False for build in Build.select(self.env, status=Build.PENDING, db=db): if self.should_delete_build(build, repos): self.log.info('Scheduling build %d for deletion', build.id) builds_to_delete.append(build) elif build.platform in platforms: build_found = True break if not build_found: self.log.debug('No pending builds.') build = None # delete any obsolete builds for build_to_delete in builds_to_delete: build_to_delete.delete(db=db) if build: build.slave = name build.slave_info.update(properties) build.status = Build.IN_PROGRESS build.update(db=db) if build or builds_to_delete: db.commit() return build
def should_delete_build(self, build, repos): config = BuildConfig.fetch(self.env, build.config) config_name = config and config.name \ or 'unknown config "%s"' % build.config platform = TargetPlatform.fetch(self.env, build.platform) # Platform may or may not exist anymore - get safe name for logging platform_name = platform and platform.name \ or 'unknown platform "%s"' % build.platform # Drop build if platform no longer exists if not platform: self.log.info( 'Dropping build of configuration "%s" at ' 'revision [%s] on %s because the platform no longer ' 'exists', config.name, build.rev, platform_name) return True # Ignore pending builds for deactived build configs if not (config and config.active): self.log.info( 'Dropping build of configuration "%s" at ' 'revision [%s] on %s because the configuration is ' 'deactivated', config_name, build.rev, platform_name) return True # Stay within the revision limits of the build config if (config.min_rev and repos.rev_older_than(build.rev, config.min_rev)) \ or (config.max_rev and repos.rev_older_than(config.max_rev, build.rev)): self.log.info( 'Dropping build of configuration "%s" at revision [%s] on ' '"%s" because it is outside of the revision range of the ' 'configuration', config.name, build.rev, platform_name) return True # If not 'build_all', drop if a more recent revision is available if not self.build_all and \ len(list(Build.select(self.env, config=build.config, min_rev_time=build.rev_time, platform=build.platform))) > 1: self.log.info( 'Dropping build of configuration "%s" at revision [%s] ' 'on "%s" because a more recent build exists', config.name, build.rev, platform_name) return True return False
def getBuildIds(self, req, config=None, rev=None, platform=None, slave=None, status=None): """Retrieve a list of build unique identifiers that match the selected criteria """ return [ build.id for build in Build.select(self.env, config=config or None, rev=rev or None, platform=platform or None, slave=slave or None, status=status or None) if build ]
def should_delete_build(self, build, repos): config = BuildConfig.fetch(self.env, build.config) config_name = config and config.name \ or 'unknown config "%s"' % build.config platform = TargetPlatform.fetch(self.env, build.platform) # Platform may or may not exist anymore - get safe name for logging platform_name = platform and platform.name \ or 'unknown platform "%s"' % build.platform # Drop build if platform no longer exists if not platform: self.log.info('Dropping build of configuration "%s" at ' 'revision [%s] on %s because the platform no longer ' 'exists', config.name, build.rev, platform_name) return True # Ignore pending builds for deactived build configs if not (config and config.active): self.log.info('Dropping build of configuration "%s" at ' 'revision [%s] on %s because the configuration is ' 'deactivated', config_name, build.rev, platform_name) return True # Stay within the revision limits of the build config if (config.min_rev and repos.rev_older_than(build.rev, config.min_rev)) \ or (config.max_rev and repos.rev_older_than(config.max_rev, build.rev)): self.log.info('Dropping build of configuration "%s" at revision [%s] on ' '"%s" because it is outside of the revision range of the ' 'configuration', config.name, build.rev, platform_name) return True # If not 'build_all', drop if a more recent revision is available if not self.build_all and \ len(list(Build.select(self.env, config=build.config, min_rev_time=build.rev_time, platform=build.platform))) > 1: self.log.info('Dropping build of configuration "%s" at revision [%s] ' 'on "%s" because a more recent build exists', config.name, build.rev, platform_name) return True return False
def get_annotation_data(self, context): add_stylesheet(context.req, 'bitten/bitten_coverage.css') resource = context.resource builds = Build.select(self.env, rev=resource.version) reports = [] for build in builds: config = BuildConfig.fetch(self.env, build.config) if not resource.id.startswith(config.path): continue reports = Report.select(self.env, build=build.id, category='coverage') path_in_config = resource.id[len(config.path):].lstrip('/') for report in reports: for item in report.items: if item.get('file') == path_in_config: # TODO should aggregate coverage across builds return item.get('line_hits', '').split() return []
def reset_orphaned_builds(self): """Reset all in-progress builds to ``PENDING`` state if they've been running so long that the configured timeout has been reached. This is used to cleanup after slaves that have unexpectedly cancelled a build without notifying the master, or are for some other reason not reporting back status updates. """ if not self.timeout: # If no timeout is set, none of the in-progress builds can be # considered orphaned return db = self.env.get_db_cnx() now = int(time.time()) for build in Build.select(self.env, status=Build.IN_PROGRESS, db=db): if now - build.last_activity < self.timeout: # This build has not reached the timeout yet, assume it's still # being executed continue self.log.info('Orphaning build %d. Last activity was %s (%s)' % \ (build.id, format_datetime(build.last_activity), pretty_timedelta(build.last_activity))) build.status = Build.PENDING build.slave = None build.slave_info = {} build.started = 0 build.stopped = 0 build.last_activity = 0 for step in list(BuildStep.select(self.env, build=build.id, db=db)): step.delete(db=db) build.update(db=db) Attachment.delete_all(self.env, 'build', build.resource.id, db) db.commit()
def _render_overview(self, req): data = {'title': 'Build Status'} show_all = False if req.args.get('show') == 'all': show_all = True data['show_all'] = show_all repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' configs = [] for config in BuildConfig.select(self.env, include_inactive=show_all): rev = config.max_rev or repos.youngest_rev try: if not _has_permission(req.perm, repos, config.path, rev=rev): continue except NoSuchNode: add_warning(req, "Configuration '%s' points to non-existing " "path '/%s' at revision '%s'. Configuration skipped." \ % (config.name, config.path, rev)) continue description = config.description if description: description = wiki_to_html(description, self.env, req) platforms_data = [] for platform in TargetPlatform.select(self.env, config=config.name): pd = { 'name': platform.name, 'id': platform.id, 'builds_pending': len(list(Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } platforms_data.append(pd) config_data = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'builds_pending' : len(list(Build.select(self.env, config=config.name, status=Build.PENDING))), 'builds_inprogress' : len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS))), 'href': req.href.build(config.name), 'builds': [], 'platforms': platforms_data } configs.append(config_data) if not config.active: continue prev_rev = None for platform, rev, build in collect_changes(repos, config): if rev != prev_rev: if prev_rev is None: chgset = repos.get_changeset(rev) config_data['youngest_rev'] = { 'id': rev, 'href': req.href.changeset(rev), 'display_rev': repos.normalize_rev(rev), 'author': chgset.author or 'anonymous', 'date': format_datetime(chgset.date), 'message': wiki_to_oneliner( shorten_line(chgset.message), self.env, req=req) } else: break prev_rev = rev if build: build_data = _get_build_data(self.env, req, build) build_data['platform'] = platform.name config_data['builds'].append(build_data) else: config_data['builds'].append({ 'platform': platform.name, 'status': 'pending' }) data['configs'] = sorted(configs, key=lambda x:x['label'].lower()) data['page_mode'] = 'overview' in_progress_builds = Build.select(self.env, status=Build.IN_PROGRESS) pending_builds = Build.select(self.env, status=Build.PENDING) data['builds_pending'] = len(list(pending_builds)) data['builds_inprogress'] = len(list(in_progress_builds)) add_link(req, 'views', req.href.build(view='inprogress'), 'In Progress Builds') add_ctxtnav(req, 'In Progress Builds', req.href.build(view='inprogress')) return data
def _render_config(self, req, config_name): db = self.env.get_db_cnx() config = BuildConfig.fetch(self.env, config_name, db=db) if not config: raise HTTPNotFound("Build configuration '%s' does not exist." \ % config_name) repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' rev = config.max_rev or repos.youngest_rev try: _has_permission(req.perm, repos, config.path, rev=rev, raise_error=True) except NoSuchNode: raise TracError("Permission checking against repository path %s " "at revision %s failed." % (config.path, rev)) data = {'title': 'Build Configuration "%s"' \ % config.label or config.name, 'page_mode': 'view_config'} add_link(req, 'up', req.href.build(), 'Build Status') description = config.description if description: description = wiki_to_html(description, self.env, req) pending_builds = list(Build.select(self.env, config=config.name, status=Build.PENDING)) inprogress_builds = list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS)) data['config'] = { 'name': config.name, 'label': config.label, 'path': config.path, 'min_rev': config.min_rev, 'min_rev_href': req.href.changeset(config.min_rev), 'max_rev': config.max_rev, 'max_rev_href': req.href.changeset(config.max_rev), 'active': config.active, 'description': description, 'browser_href': req.href.browser(config.path), 'builds_pending' : len(pending_builds), 'builds_inprogress' : len(inprogress_builds) } context = Context.from_request(req, config.resource) data['context'] = context data['config']['attachments'] = AttachmentModule(self.env).attachment_data(context) platforms = list(TargetPlatform.select(self.env, config=config_name, db=db)) data['config']['platforms'] = [ { 'name': platform.name, 'id': platform.id, 'builds_pending': len(list(Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } for platform in platforms ] has_reports = False for report in Report.select(self.env, config=config.name, db=db): has_reports = True break if has_reports: chart_generators = [] report_categories = list(self._report_categories_for_config(config)) for generator in ReportChartController(self.env).generators: for category in generator.get_supported_categories(): if category in report_categories: chart_generators.append({ 'href': req.href.build(config.name, 'chart/' + category), 'category': category, 'style': self.config.get('bitten', 'chart_style'), }) data['config']['charts'] = chart_generators page = max(1, int(req.args.get('page', 1))) more = False data['page_number'] = page repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' builds_per_page = 12 * len(platforms) idx = 0 builds = {} revisions = [] for platform, rev, build in collect_changes(repos, config): if idx >= page * builds_per_page: more = True break elif idx >= (page - 1) * builds_per_page: if rev not in builds: revisions.append(rev) builds.setdefault(rev, {}) builds[rev].setdefault('href', req.href.changeset(rev)) builds[rev].setdefault('display_rev', repos.normalize_rev(rev)) if build and build.status != Build.PENDING: build_data = _get_build_data(self.env, req, build) build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped or int(time.time()), utc) - \ to_datetime(step.started, utc), 'status': _step_status_label[step.status], 'cls': _step_status_label[step.status].replace(' ', '-'), 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds[rev][platform.id] = build_data idx += 1 data['config']['builds'] = builds data['config']['revisions'] = revisions if page > 1: if page == 2: prev_href = req.href.build(config.name) else: prev_href = req.href.build(config.name, page=page - 1) add_link(req, 'prev', prev_href, 'Previous Page') if more: next_href = req.href.build(config.name, page=page + 1) add_link(req, 'next', next_href, 'Next Page') if arity(prevnext_nav) == 4: # Trac 0.12 compat, see #450 prevnext_nav(req, 'Previous Page', 'Next Page') else: prevnext_nav (req, 'Page') return data
def _render_config(self, req, config_name): db = self.env.get_db_cnx() config = BuildConfig.fetch(self.env, config_name, db=db) if not config: raise HTTPNotFound("Build configuration '%s' does not exist." \ % config_name) repos = self.env.get_repository(req.authname) repos.authz.assert_permission(config.branch) data = {'title': 'Build Configuration "%s"' \ % config.label or config.name, 'page_mode': 'view_config'} add_link(req, 'up', req.href.build(), 'Build Status') description = config.description if description: description = wiki_to_html(description, self.env, req) pending_builds = list( Build.select(self.env, config=config.name, status=Build.PENDING)) inprogress_builds = list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS)) rev = '' for b in repos.git.get_branches(): if b[0] == config.branch: rev = b[1] break data['config'] = { 'name': config.name, 'label': config.label, 'branch': config.branch, 'active': config.active, 'description': description, 'browser_href': req.href.browser(rev=rev), 'builds_pending': len(pending_builds), 'builds_inprogress': len(inprogress_builds) } context = Context.from_request(req, config.resource) data['context'] = context data['config']['attachments'] = AttachmentModule( self.env).attachment_data(context) platforms = list( TargetPlatform.select(self.env, config=config_name, db=db)) data['config']['platforms'] = [{ 'name': platform.name, 'id': platform.id, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } for platform in platforms] has_reports = False for report in Report.select(self.env, config=config.name, db=db): has_reports = True break if has_reports: chart_generators = [] for generator in ReportChartController(self.env).generators: for category in generator.get_supported_categories(): chart_generators.append({ 'href': req.href.build(config.name, 'chart/' + category) }) data['config']['charts'] = chart_generators charts_license = self.config.get('bitten', 'charts_license') if charts_license: data['config']['charts_license'] = charts_license page = max(1, int(req.args.get('page', 1))) more = False data['page_number'] = page repos = self.env.get_repository(req.authname) builds_per_page = 12 * len(platforms) idx = 0 builds = {} for platform, rev, build in collect_changes(repos, config): if idx >= page * builds_per_page: more = True break elif idx >= (page - 1) * builds_per_page: builds.setdefault(rev, {}) builds[rev].setdefault('href', req.href.changeset(rev)) if build and build.status != Build.PENDING: build_data = _get_build_data(self.env, req, build) build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped, utc) - \ to_datetime(step.started, utc), 'failed': not step.successful, 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds[rev][platform.id] = build_data idx += 1 data['config']['builds'] = builds if page > 1: if page == 2: prev_href = req.href.build(config.name) else: prev_href = req.href.build(config.name, page=page - 1) add_link(req, 'prev', prev_href, 'Previous Page') if more: next_href = req.href.build(config.name, page=page + 1) add_link(req, 'next', next_href, 'Next Page') prevnext_nav(req, 'Page') return data
def _render_inprogress(self, req): data = {'title': 'In Progress Builds', 'page_mode': 'view-inprogress'} db = self.env.get_db_cnx() repos = self.env.get_repository(req.authname) configs = [] for config in BuildConfig.select(self.env, include_inactive=False): if not repos.authz.has_permission(config.branch): continue self.log.debug(config.name) if not config.active: continue in_progress_builds = Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, db=db) current_builds = 0 builds = [] # sort correctly by revision. for build in sorted(in_progress_builds, cmp=lambda x, y: int(y.rev) - int(x.rev)): rev = build.rev build_data = _get_build_data(self.env, req, build) build_data['rev'] = rev build_data['rev_href'] = req.href.changeset(rev) platform = TargetPlatform.fetch(self.env, build.platform) build_data['platform'] = platform.name build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped, utc) - \ to_datetime(step.started, utc), 'failed': not step.successful, 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds.append(build_data) current_builds += 1 if current_builds == 0: continue description = config.description if description: description = wiki_to_html(description, self.env, req) configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'branch': config.branch, 'description': description, 'href': req.href.build(config.name), 'builds': builds }) data['configs'] = configs return data
def _render_overview(self, req): data = {'title': 'Build Status'} show_all = False if req.args.get('show') == 'all': show_all = True data['show_all'] = show_all repos = self.env.get_repository(req.authname) configs = [] for config in BuildConfig.select(self.env, include_inactive=show_all): if not repos.authz.has_permission(config.branch): continue description = config.description if description: description = wiki_to_html(description, self.env, req) platforms_data = [] for platform in TargetPlatform.select(self.env, config=config.name): pd = { 'name': platform.name, 'id': platform.id, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } platforms_data.append(pd) config_data = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'branch': config.branch, 'description': description, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS))), 'href': req.href.build(config.name), 'builds': [], 'platforms': platforms_data } configs.append(config_data) if not config.active: continue prev_rev = None for platform, rev, build in collect_changes(repos, config): if rev != prev_rev: if prev_rev is None: chgset = repos.get_changeset(rev) config_data['youngest_rev'] = { 'id': rev, 'href': req.href.changeset(rev), 'author': chgset.author or 'anonymous', 'date': format_datetime(chgset.date), 'message': wiki_to_oneliner(shorten_line(chgset.message), self.env, req=req) } else: break prev_rev = rev if build: build_data = _get_build_data(self.env, req, build) build_data['platform'] = platform.name config_data['builds'].append(build_data) else: config_data['builds'].append({ 'platform': platform.name, 'status': 'pending' }) data['configs'] = configs data['page_mode'] = 'overview' in_progress_builds = Build.select(self.env, status=Build.IN_PROGRESS) pending_builds = Build.select(self.env, status=Build.PENDING) data['builds_pending'] = len(list(pending_builds)) data['builds_inprogress'] = len(list(in_progress_builds)) add_link(req, 'views', req.href.build(view='inprogress'), 'In Progress Builds') add_ctxtnav(req, 'In Progress Builds', req.href.build(view='inprogress')) return data
def _render_config(self, req, config_name): db = self.env.get_db_cnx() config = BuildConfig.fetch(self.env, config_name, db=db) if not config: raise HTTPNotFound("Build configuration '%s' does not exist." \ % config_name) repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' rev = config.max_rev or repos.youngest_rev try: _has_permission(req.perm, repos, config.path, rev=rev, raise_error=True) except NoSuchNode: raise TracError("Permission checking against repository path %s " "at revision %s failed." % (config.path, rev)) data = {'title': 'Build Configuration "%s"' \ % config.label or config.name, 'page_mode': 'view_config'} add_link(req, 'up', req.href.build(), 'Build Status') description = config.description if description: description = wiki_to_html(description, self.env, req) pending_builds = list( Build.select(self.env, config=config.name, status=Build.PENDING)) inprogress_builds = list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS)) data['config'] = { 'name': config.name, 'label': config.label, 'path': config.path, 'min_rev': config.min_rev, 'min_rev_href': req.href.changeset(config.min_rev), 'max_rev': config.max_rev, 'max_rev_href': req.href.changeset(config.max_rev), 'active': config.active, 'description': description, 'browser_href': req.href.browser(config.path), 'builds_pending': len(pending_builds), 'builds_inprogress': len(inprogress_builds) } context = Context.from_request(req, config.resource) data['context'] = context data['config']['attachments'] = AttachmentModule( self.env).attachment_data(context) platforms = list( TargetPlatform.select(self.env, config=config_name, db=db)) data['config']['platforms'] = [{ 'name': platform.name, 'id': platform.id, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } for platform in platforms] has_reports = False for report in Report.select(self.env, config=config.name, db=db): has_reports = True break if has_reports: chart_generators = [] report_categories = list( self._report_categories_for_config(config)) for generator in ReportChartController(self.env).generators: for category in generator.get_supported_categories(): if category in report_categories: chart_generators.append({ 'href': req.href.build(config.name, 'chart/' + category), 'category': category, 'style': self.config.get('bitten', 'chart_style'), }) data['config']['charts'] = chart_generators page = max(1, int(req.args.get('page', 1))) more = False data['page_number'] = page repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' builds_per_page = 12 * len(platforms) idx = 0 builds = {} revisions = [] for platform, rev, build in collect_changes(repos, config): if idx >= page * builds_per_page: more = True break elif idx >= (page - 1) * builds_per_page: if rev not in builds: revisions.append(rev) builds.setdefault(rev, {}) builds[rev].setdefault('href', req.href.changeset(rev)) builds[rev].setdefault('display_rev', repos.normalize_rev(rev)) if build and build.status != Build.PENDING: build_data = _get_build_data(self.env, req, build) build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped or int(time.time()), utc) - \ to_datetime(step.started, utc), 'status': _step_status_label[step.status], 'cls': _step_status_label[step.status].replace(' ', '-'), 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds[rev][platform.id] = build_data idx += 1 data['config']['builds'] = builds data['config']['revisions'] = revisions if page > 1: if page == 2: prev_href = req.href.build(config.name) else: prev_href = req.href.build(config.name, page=page - 1) add_link(req, 'prev', prev_href, 'Previous Page') if more: next_href = req.href.build(config.name, page=page + 1) add_link(req, 'next', next_href, 'Next Page') if arity(prevnext_nav) == 4: # Trac 0.12 compat, see #450 prevnext_nav(req, 'Previous Page', 'Next Page') else: prevnext_nav(req, 'Page') return data
continue # Make sure the repository directory isn't empty at this # revision old_node = repos.get_node(path, rev) is_empty = True for entry in old_node.get_entries(): is_empty = False break if is_empty: continue # For every target platform, check whether there's a build # of this revision for platform in TargetPlatform.select(env, config.name, db=db): builds = list(Build.select(env, config.name, rev, platform.id, db=db)) if builds: build = builds[0] else: build = None yield platform, rev, build class BuildQueue(object): """Enapsulates the build queue of an environment. A build queue manages the the registration of build slaves and detection of repository revisions that need to be built. """
def _render_overview(self, req): data = {'title': 'Build Status'} show_all = False if req.args.get('show') == 'all': show_all = True data['show_all'] = show_all repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' configs = [] for config in BuildConfig.select(self.env, include_inactive=show_all): rev = config.max_rev or repos.youngest_rev try: if not _has_permission(req.perm, repos, config.path, rev=rev): continue except NoSuchNode: add_warning(req, "Configuration '%s' points to non-existing " "path '/%s' at revision '%s'. Configuration skipped." \ % (config.name, config.path, rev)) continue description = config.description if description: description = wiki_to_html(description, self.env, req) platforms_data = [] for platform in TargetPlatform.select(self.env, config=config.name): pd = { 'name': platform.name, 'id': platform.id, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } platforms_data.append(pd) config_data = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS))), 'href': req.href.build(config.name), 'builds': [], 'platforms': platforms_data } configs.append(config_data) if not config.active: continue prev_rev = None for platform, rev, build in collect_changes(repos, config): if rev != prev_rev: if prev_rev is None: chgset = repos.get_changeset(rev) config_data['youngest_rev'] = { 'id': rev, 'href': req.href.changeset(rev), 'display_rev': repos.normalize_rev(rev), 'author': chgset.author or 'anonymous', 'date': format_datetime(chgset.date), 'message': wiki_to_oneliner(shorten_line(chgset.message), self.env, req=req) } else: break prev_rev = rev if build: build_data = _get_build_data(self.env, req, build) build_data['platform'] = platform.name config_data['builds'].append(build_data) else: config_data['builds'].append({ 'platform': platform.name, 'status': 'pending' }) data['configs'] = sorted(configs, key=lambda x: x['label'].lower()) data['page_mode'] = 'overview' in_progress_builds = Build.select(self.env, status=Build.IN_PROGRESS) pending_builds = Build.select(self.env, status=Build.PENDING) data['builds_pending'] = len(list(pending_builds)) data['builds_inprogress'] = len(list(in_progress_builds)) add_link(req, 'views', req.href.build(view='inprogress'), 'In Progress Builds') add_ctxtnav(req, 'In Progress Builds', req.href.build(view='inprogress')) return data
def _render_inprogress(self, req): data = {'title': 'In Progress Builds', 'page_mode': 'view-inprogress'} db = self.env.get_db_cnx() repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' configs = [] for config in BuildConfig.select(self.env, include_inactive=False): rev = config.max_rev or repos.youngest_rev try: if not _has_permission(req.perm, repos, config.path, rev=rev): continue except NoSuchNode: add_warning(req, "Configuration '%s' points to non-existing " "path '/%s' at revision '%s'. Configuration skipped." \ % (config.name, config.path, rev)) continue self.log.debug(config.name) if not config.active: continue in_progress_builds = Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, db=db) current_builds = 0 builds = [] # sort correctly by revision. for build in sorted( in_progress_builds, cmp=lambda x, y: int(y.rev_time) - int(x.rev_time)): rev = build.rev build_data = _get_build_data(self.env, req, build) build_data['rev'] = rev build_data['rev_href'] = req.href.changeset(rev) platform = TargetPlatform.fetch(self.env, build.platform) build_data['platform'] = platform.name build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped or int(time.time()), utc) - \ to_datetime(step.started, utc), 'status': _step_status_label[step.status], 'cls': _step_status_label[step.status].replace(' ', '-'), 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds.append(build_data) current_builds += 1 if current_builds == 0: continue description = config.description if description: description = wiki_to_html(description, self.env, req) configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'href': req.href.build(config.name), 'builds': builds }) data['configs'] = sorted(configs, key=lambda x: x['label'].lower()) return data
def getBuildIds(self, req, config=None, rev=None, platform=None, slave=None, status=None): """Retrieve a list of build unique identifiers that match the selected criteria """ return [build.id for build in Build.select(self.env, config=config or None, rev=rev or None, platform=platform or None, slave=slave or None, status=status or None) if build]
def _render_inprogress(self, req): data = {'title': 'In Progress Builds', 'page_mode': 'view-inprogress'} db = self.env.get_db_cnx() repos = self.env.get_repository(authname=req.authname) assert repos, 'No "(default)" Repository: Add a repository or alias ' \ 'named "(default)" to Trac.' configs = [] for config in BuildConfig.select(self.env, include_inactive=False): rev = config.max_rev or repos.youngest_rev try: if not _has_permission(req.perm, repos, config.path, rev=rev): continue except NoSuchNode: add_warning(req, "Configuration '%s' points to non-existing " "path '/%s' at revision '%s'. Configuration skipped." \ % (config.name, config.path, rev)) continue self.log.debug(config.name) if not config.active: continue in_progress_builds = Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, db=db) current_builds = 0 builds = [] # sort correctly by revision. for build in sorted(in_progress_builds, cmp=lambda x, y: int(y.rev_time) - int(x.rev_time)): rev = build.rev build_data = _get_build_data(self.env, req, build) build_data['rev'] = rev build_data['rev_href'] = req.href.changeset(rev) platform = TargetPlatform.fetch(self.env, build.platform) build_data['platform'] = platform.name build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped or int(time.time()), utc) - \ to_datetime(step.started, utc), 'status': _step_status_label[step.status], 'cls': _step_status_label[step.status].replace(' ', '-'), 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds.append(build_data) current_builds += 1 if current_builds == 0: continue description = config.description if description: description = wiki_to_html(description, self.env, req) configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'href': req.href.build(config.name), 'builds': builds }) data['configs'] = sorted(configs, key=lambda x:x['label'].lower()) return data
def _render_overview(self, req): data = {'title': 'Build Status'} show_all = False if req.args.get('show') == 'all': show_all = True data['show_all'] = show_all repos = self.env.get_repository(req.authname) if hasattr(repos, 'sync'): repos.sync() configs = [] for config in BuildConfig.select(self.env, include_inactive=show_all): if not repos.authz.has_permission(config.path): continue description = config.description if description: description = wiki_to_html(description, self.env, req) platforms_data = [] for platform in TargetPlatform.select(self.env, config=config.name): pd = { 'name': platform.name, 'id': platform.id, 'builds_pending': len(list(Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } platforms_data.append(pd) config_data = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'builds_pending' : len(list(Build.select(self.env, config=config.name, status=Build.PENDING))), 'builds_inprogress' : len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS))), 'href': req.href.build(config.name), 'builds': [], 'platforms': platforms_data } configs.append(config_data) if not config.active: continue prev_rev = None for platform, rev, build in collect_changes(repos, config): if rev != prev_rev: if prev_rev is None: chgset = repos.get_changeset(rev) config_data['youngest_rev'] = { 'id': rev, 'href': req.href.changeset(rev), 'author': chgset.author or 'anonymous', 'date': format_datetime(chgset.date), 'message': wiki_to_oneliner( shorten_line(chgset.message), self.env, req=req) } else: break prev_rev = rev if build: build_data = _get_build_data(self.env, req, build) build_data['platform'] = platform.name config_data['builds'].append(build_data) else: config_data['builds'].append({ 'platform': platform.name, 'status': 'pending' }) data['configs'] = configs data['page_mode'] = 'overview' in_progress_builds = Build.select(self.env, status=Build.IN_PROGRESS) pending_builds = Build.select(self.env, status=Build.PENDING) data['builds_pending'] = len(list(pending_builds)) data['builds_inprogress'] = len(list(in_progress_builds)) add_link(req, 'views', req.href.build(view='inprogress'), 'In Progress Builds') add_ctxtnav(req, 'In Progress Builds', req.href.build(view='inprogress')) return data
def _render_config(self, req, config_name): db = self.env.get_db_cnx() config = BuildConfig.fetch(self.env, config_name, db=db) repos = self.env.get_repository(req.authname) if hasattr(repos, 'sync'): repos.sync() repos.authz.assert_permission(config.path) data = {'title': 'Build Configuration "%s"' \ % config.label or config.name, 'page_mode': 'view_config'} add_link(req, 'up', req.href.build(), 'Build Status') description = config.description if description: description = wiki_to_html(description, self.env, req) pending_builds = list(Build.select(self.env, config=config.name, status=Build.PENDING)) inprogress_builds = list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS)) data['config'] = { 'name': config.name, 'label': config.label, 'path': config.path, 'min_rev': config.min_rev, 'min_rev_href': req.href.changeset(config.min_rev), 'max_rev': config.max_rev, 'max_rev_href': req.href.changeset(config.max_rev), 'active': config.active, 'description': description, 'browser_href': req.href.browser(config.path), 'builds_pending' : len(pending_builds), 'builds_inprogress' : len(inprogress_builds) } platforms = list(TargetPlatform.select(self.env, config=config_name, db=db)) data['config']['platforms'] = [ { 'name': platform.name, 'id': platform.id, 'builds_pending': len(list(Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len(list(Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } for platform in platforms ] has_reports = False for report in Report.select(self.env, config=config.name, db=db): has_reports = True break if has_reports: chart_generators = [] for generator in ReportChartController(self.env).generators: for category in generator.get_supported_categories(): chart_generators.append({ 'href': req.href.build(config.name, 'chart/' + category) }) data['config']['charts'] = chart_generators charts_license = self.config.get('bitten', 'charts_license') if charts_license: data['config']['charts_license'] = charts_license page = max(1, int(req.args.get('page', 1))) more = False data['page_number'] = page repos = self.env.get_repository(req.authname) if hasattr(repos, 'sync'): repos.sync() builds_per_page = 12 * len(platforms) idx = 0 builds = {} for platform, rev, build in collect_changes(repos, config): if idx >= page * builds_per_page: more = True break elif idx >= (page - 1) * builds_per_page: builds.setdefault(rev, {}) builds[rev].setdefault('href', req.href.changeset(rev)) if build and build.status != Build.PENDING: build_data = _get_build_data(self.env, req, build) build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': datetime.fromtimestamp(step.stopped) - \ datetime.fromtimestamp(step.started), 'failed': not step.successful, 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds[rev][platform.id] = build_data idx += 1 data['config']['builds'] = builds if page > 1: if page == 2: prev_href = req.href.build(config.name) else: prev_href = req.href.build(config.name, page=page - 1) add_link(req, 'prev', prev_href, 'Previous Page') if more: next_href = req.href.build(config.name, page=page + 1) add_link(req, 'next', next_href, 'Next Page') prevnext_nav(req, 'Page') return data
def _render_inprogress(self, req): data = {'title': 'In Progress Builds', 'page_mode': 'view-inprogress'} db = self.env.get_db_cnx() repos = self.env.get_repository(req.authname) if hasattr(repos, 'sync'): repos.sync() configs = [] for config in BuildConfig.select(self.env, include_inactive=False): if not repos.authz.has_permission(config.path): continue self.log.debug(config.name) if not config.active: continue in_progress_builds = Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, db=db) current_builds = 0 builds = [] # sort correctly by revision. for build in sorted(in_progress_builds, cmp=lambda x, y: int(y.rev) - int(x.rev)): rev = build.rev build_data = _get_build_data(self.env, req, build) build_data['rev'] = rev build_data['rev_href'] = req.href.changeset(rev) platform = TargetPlatform.fetch(self.env, build.platform) build_data['platform'] = platform.name build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': datetime.fromtimestamp(step.stopped) - \ datetime.fromtimestamp(step.started), 'failed': not step.successful, 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds.append(build_data) current_builds += 1 if current_builds == 0: continue description = config.description if description: description = wiki_to_html(description, self.env, req) configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'description': description, 'href': req.href.build(config.name), 'builds': builds }) data['configs'] = configs return data