def test_process_remove_platforms(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() platform = TargetPlatform(self.env, config='foo', name='any') platform.insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, authname='joe', chrome={'warnings': [], 'notices': []}, href=Href('/'), args={'remove': '', 'sel': str(platform.id)}) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.fail('Expected RequestDone') except RequestDone: self.assertEquals(['Target Platform(s) Removed.'], req.chrome['notices']) self.assertEqual('http://example.org/admin/bitten/configs/foo', redirected_to[0]) platforms = list(TargetPlatform.select(self.env, config='foo')) self.assertEqual(0, len(platforms))
def test_process_update_platform_cancel(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() platform = TargetPlatform(self.env, config='foo', name='any') platform.insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, authname='joe', args={'cancel': '', 'edit': '', 'name': 'Changed', 'property_0': 'family', 'pattern_0': 'posix'}) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo/%d' % platform.id) self.fail('Expected RequestDone') except RequestDone: self.assertEqual('http://example.org/admin/bitten/configs/foo', redirected_to[0]) platforms = list(TargetPlatform.select(self.env, config='foo')) self.assertEqual(1, len(platforms)) self.assertEqual('any', platforms[0].name) self.assertEqual([], platforms[0].rules)
def test_select(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.executemany("INSERT INTO bitten_platform (config,name) " "VALUES (%s,%s)", [('test', 'Windows'), ('test', 'Mac OS X')]) platforms = list(TargetPlatform.select(self.env, config='test')) self.assertEqual(2, len(platforms))
def test_process_remove_platforms(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() platform = TargetPlatform(self.env, config='foo', name='any') platform.insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, authname='joe', chrome={ 'warnings': [], 'notices': [] }, href=Href('/'), args={ 'remove': '', 'sel': str(platform.id) }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.fail('Expected RequestDone') except RequestDone: self.assertEquals(['Target Platform(s) Removed.'], req.chrome['notices']) self.assertEqual('http://example.org/admin/bitten/configs/foo', redirected_to[0]) platforms = list(TargetPlatform.select(self.env, config='foo')) self.assertEqual(0, len(platforms))
def match_slave(self, name, properties): """Match a build slave against available target platforms. :param name: the name of the slave :type name: `basestring` :param properties: the slave configuration :type properties: `dict` :return: the list of platforms the slave matched """ platforms = [] for config in BuildConfig.select(self.env): for platform in TargetPlatform.select(self.env, config=config.name): match = True for propname, pattern in ifilter(None, platform.rules): try: propvalue = properties.get(propname) if not propvalue or not re.match( pattern, propvalue, re.I): match = False break except re.error: self.log.error( 'Invalid platform matching pattern "%s"', pattern, exc_info=True) match = False break if match: self.log.debug( 'Slave %r matched target platform %r of ' 'build configuration %r', name, platform.name, config.name) platforms.append(platform) if not platforms: self.log.warning('Slave %r matched none of the target platforms', name) return platforms
def test_process_update_platform_cancel(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() platform = TargetPlatform(self.env, config='foo', name='any') platform.insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, authname='joe', args={ 'cancel': '', 'edit': '', 'name': 'Changed', 'property_0': 'family', 'pattern_0': 'posix' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo/%d' % platform.id) self.fail('Expected RequestDone') except RequestDone: self.assertEqual('http://example.org/admin/bitten/configs/foo', redirected_to[0]) platforms = list(TargetPlatform.select(self.env, config='foo')) self.assertEqual(1, len(platforms)) self.assertEqual('any', platforms[0].name) self.assertEqual([], platforms[0].rules)
def match_slave(self, name, properties): """Match a build slave against available target platforms. :param name: the name of the slave :type name: `basestring` :param properties: the slave configuration :type properties: `dict` :return: the list of platforms the slave matched """ platforms = [] for config in BuildConfig.select(self.env): for platform in TargetPlatform.select(self.env, config=config.name): match = True for propname, pattern in ifilter(None, platform.rules): try: propvalue = properties.get(propname) if not propvalue or not re.match(pattern, propvalue, re.I): match = False break except re.error: self.log.error('Invalid platform matching pattern "%s"', pattern, exc_info=True) match = False break if match: self.log.debug('Slave %r matched target platform %r of ' 'build configuration %r', name, platform.name, config.name) platforms.append(platform) if not platforms: self.log.warning('Slave %r matched none of the target platforms', name) return platforms
def render_admin_panel(self, req, cat, page, path_info): data = {} # Analyze url try: config_name, platform_id = path_info.split('/', 1) except: config_name = path_info platform_id = None if config_name: # Existing build config if platform_id or ( # Editing or creating one of the config's target platforms req.method == 'POST' and 'new' in req.args): if platform_id: # Editing target platform platform_id = int(platform_id) platform = TargetPlatform.fetch(self.env, platform_id) if req.method == 'POST': if 'cancel' in req.args or \ self._update_platform(req, platform): req.redirect(req.abs_href.admin(cat, page, config_name)) else: # creating target platform if req.method == 'POST': if 'add' in req.args: self._create_platform(req, config_name) req.redirect(req.abs_href.admin(cat, page, config_name)) elif 'cancel' in req.args: req.redirect(req.abs_href.admin(cat, page, config_name)) platform = TargetPlatform(self.env, config=config_name) # Set up template variables data['platform'] = { 'id': platform.id, 'name': platform.name, 'exists': platform.exists, 'rules': [ {'property': propname, 'pattern': pattern} for propname, pattern in platform.rules ] or [('', '')] } else: # Editing existing build config itself config = BuildConfig.fetch(self.env, config_name) platforms = list(TargetPlatform.select(self.env, config=config.name)) if req.method == 'POST': if 'add' in req.args: # Add target platform platform = self._create_platform(req, config) req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'remove' in req.args: # Remove selected platforms self._remove_platforms(req) req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'save' in req.args: # Save this build config self._update_config(req, config) req.redirect(req.abs_href.admin(cat, page)) # Prepare template variables data['config'] = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'min_rev': config.min_rev, 'max_rev': config.max_rev, 'description': config.description, 'recipe': config.recipe, 'platforms': [{ 'name': platform.name, 'id': platform.id, 'href': req.href.admin('bitten', 'configs', config.name, platform.id), 'rules': [{'property': propname, 'pattern': pattern} for propname, pattern in platform.rules] } for platform in platforms] } else: # At the top level build config list if req.method == 'POST': if 'add' in req.args: # Add build config config = self._create_config(req) req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'remove' in req.args: # Remove selected build configs self._remove_configs(req) elif 'apply' in req.args: # Update active state of configs self._activate_configs(req) req.redirect(req.abs_href.admin(cat, page)) # Prepare template variables configs = [] for config in BuildConfig.select(self.env, include_inactive=True): configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'min_rev': config.min_rev, 'max_rev': config.max_rev, 'href': req.href.admin('bitten', 'configs', config.name), }) data['configs'] = configs add_stylesheet(req, 'bitten/admin.css') add_script(req, 'common/js/suggest.js') return 'bitten_admin_configs.html', 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_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_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) 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_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_admin_panel(self, req, cat, page, path_info): data = {} # Analyze url try: config_name, platform_id = path_info.split('/', 1) except: config_name = path_info platform_id = None if config_name: # Existing build config warnings = [] if platform_id or ( # Editing or creating one of the config's target platforms req.method == 'POST' and 'new' in req.args): if platform_id: # Editing target platform platform_id = int(platform_id) platform = TargetPlatform.fetch(self.env, platform_id) if req.method == 'POST': if 'cancel' in req.args or \ self._update_platform(req, platform): req.redirect(req.abs_href.admin(cat, page, config_name)) else: # creating target platform platform = self._create_platform(req, config_name) req.redirect(req.abs_href.admin(cat, page, config_name, platform.id)) # Set up template variables data['platform'] = { 'id': platform.id, 'name': platform.name, 'exists': platform.exists, 'rules': [ {'property': propname, 'pattern': pattern} for propname, pattern in platform.rules ] or [('', '')] } else: # Editing existing build config itself config = BuildConfig.fetch(self.env, config_name) platforms = list(TargetPlatform.select(self.env, config=config.name)) if req.method == 'POST': if 'remove' in req.args: # Remove selected platforms self._remove_platforms(req) add_notice(req, "Target Platform(s) Removed.") req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'save' in req.args: # Save this build config warnings = self._update_config(req, config) if not warnings: add_notice(req, "Configuration Saved.") req.redirect(req.abs_href.admin(cat, page, config.name)) for warning in warnings: add_warning(req, warning) # FIXME: Deprecation notice for old namespace. # Remove notice code when migration to new namespace is complete if 'http://bitten.cmlenz.net/tools/' in config.recipe: add_notice(req, "Recipe uses a deprecated namespace. " "Replace 'http://bitten.cmlenz.net/tools/' with " "'http://bitten.edgewall.org/tools/'.") # Add a notice if configuration is not active if not warnings and not config.active and config.recipe: add_notice(req, "Configuration is not active. Activate " "from main 'Configurations' listing to enable it.") # Prepare template variables data['config'] = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'min_rev': config.min_rev, 'max_rev': config.max_rev, 'description': config.description, 'recipe': config.recipe, 'platforms': [{ 'name': platform.name, 'id': platform.id, 'href': req.href.admin('bitten', 'configs', config.name, platform.id), 'rules': [{'property': propname, 'pattern': pattern} for propname, pattern in platform.rules] } for platform in platforms] } else: # At the top level build config list if req.method == 'POST': if 'add' in req.args: # Add build config config = self._create_config(req) req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'remove' in req.args: # Remove selected build configs self._remove_configs(req) elif 'apply' in req.args: # Update active state of configs self._activate_configs(req) req.redirect(req.abs_href.admin(cat, page)) # Prepare template variables configs = [] for config in BuildConfig.select(self.env, include_inactive=True): configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'path': config.path, 'min_rev': config.min_rev, 'max_rev': config.max_rev, 'href': req.href.admin('bitten', 'configs', config.name), 'recipe': config.recipe and True or False }) data['configs'] = sorted(configs, key=lambda x:x['label'].lower()) add_stylesheet(req, 'bitten/admin.css') add_script(req, 'common/js/suggest.js') return 'bitten_admin_configs.html', data
def render_admin_panel(self, req, cat, page, path_info): data = {} # Analyze url try: config_name, platform_id = path_info.split('/', 1) except: config_name = path_info platform_id = None if config_name: # Existing build config warnings = [] if platform_id or ( # Editing or creating one of the config's target platforms req.method == 'POST' and 'new' in req.args): if platform_id: # Editing target platform platform_id = int(platform_id) platform = TargetPlatform.fetch(self.env, platform_id) if req.method == 'POST': if 'cancel' in req.args or \ self._update_platform(req, platform): req.redirect( req.abs_href.admin(cat, page, config_name)) else: # creating target platform platform = self._create_platform(req, config_name) req.redirect( req.abs_href.admin(cat, page, config_name, platform.id)) # Set up template variables data['platform'] = { 'id': platform.id, 'name': platform.name, 'exists': platform.exists, 'rules': [{ 'property': propname, 'pattern': pattern } for propname, pattern in platform.rules] or [('', '')] } else: # Editing existing build config itself config = BuildConfig.fetch(self.env, config_name) platforms = list( TargetPlatform.select(self.env, config=config.name)) if req.method == 'POST': if 'remove' in req.args: # Remove selected platforms self._remove_platforms(req) add_notice(req, "Target Platform(s) Removed.") req.redirect(req.abs_href.admin( cat, page, config.name)) elif 'save' in req.args: # Save this build config warnings = self._update_config(req, config) if not warnings: add_notice(req, "Configuration Saved.") req.redirect(req.abs_href.admin( cat, page, config.name)) for warning in warnings: add_warning(req, warning) # FIXME: Deprecation notice for old namespace. # Remove notice code when migration to new namespace is complete if 'http://bitten.cmlenz.net/tools/' in config.recipe: add_notice( req, "Recipe uses a deprecated namespace. " "Replace 'http://bitten.cmlenz.net/tools/' with " "'http://bitten.edgewall.org/tools/'.") # Add a notice if configuration is not active if not warnings and not config.active and config.recipe: add_notice( req, "Configuration is not active. Activate " "from main 'Configurations' listing to enable it.") # Prepare template variables data['config'] = { 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'branch': config.branch, 'description': config.description, 'recipe': config.recipe, 'platforms': [{ 'name': platform.name, 'id': platform.id, 'href': req.href.admin('bitten', 'configs', config.name, platform.id), 'rules': [{ 'property': propname, 'pattern': pattern } for propname, pattern in platform.rules] } for platform in platforms] } else: # At the top level build config list if req.method == 'POST': if 'add' in req.args: # Add build config config = self._create_config(req) req.redirect(req.abs_href.admin(cat, page, config.name)) elif 'remove' in req.args: # Remove selected build configs self._remove_configs(req) elif 'apply' in req.args: # Update active state of configs self._activate_configs(req) req.redirect(req.abs_href.admin(cat, page)) # Prepare template variables configs = [] for config in BuildConfig.select(self.env, include_inactive=True): configs.append({ 'name': config.name, 'label': config.label or config.name, 'active': config.active, 'branch': config.branch, 'href': req.href.admin('bitten', 'configs', config.name), 'recipe': config.recipe and True or False }) data['configs'] = configs add_stylesheet(req, 'bitten/admin.css') add_script(req, 'common/js/suggest.js') return 'bitten_admin_configs.html', 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
if config.max_rev and repos.rev_older_than(config.max_rev, rev): 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