view bitten/main.py @ 645:8c824b14e1c5

0.6dev: Switching `master.py` to use new `self._send_response()` and `self._send_error() methods. Simplifies code, but most importantly for errors it allows a consistent method for transmitting plain-text error messages to the slave (that the slave will now output as part of debug logging). Raising Trac HTTP* errors actually causes full rendering of an HTML error page as response, which is both inefficient as well as making it near-impossible to extract the 'hidden' message from the master.
author osimons
date Mon, 24 Aug 2009 12:00:43 +0000
parents f3bb52da9e3c
children 166e1d272623
line wrap: on
line source
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
# Copyright (C) 2007 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://bitten.edgewall.org/wiki/License.

import inspect
import os
import textwrap

from trac.attachment import ILegacyAttachmentPolicyDelegate
from trac.core import *
from trac.db import DatabaseManager
from trac.env import IEnvironmentSetupParticipant
from trac.perm import IPermissionRequestor
from trac.resource import IResourceManager
from trac.wiki import IWikiSyntaxProvider
from bitten.api import IBuildListener
from bitten.model import schema, schema_version, Build, BuildConfig

__all__ = ['BuildSystem']
__docformat__ = 'restructuredtext en'


class BuildSystem(Component):

    implements(IEnvironmentSetupParticipant, IPermissionRequestor,
               IWikiSyntaxProvider, IResourceManager,
               ILegacyAttachmentPolicyDelegate)

    listeners = ExtensionPoint(IBuildListener)

    # IEnvironmentSetupParticipant methods

    def environment_created(self):
        # Create the required tables
        db = self.env.get_db_cnx()
        connector, _ = DatabaseManager(self.env)._get_connector()
        cursor = db.cursor()
        for table in schema:
            for stmt in connector.to_sql(table):
                cursor.execute(stmt)

        # Insert a global version flag
        cursor.execute("INSERT INTO system (name,value) "
                       "VALUES ('bitten_version',%s)", (schema_version,))

        # Create the directory for storing snapshot archives
        snapshots_dir = os.path.join(self.env.path, 'snapshots')
        os.mkdir(snapshots_dir)

        db.commit()

    def environment_needs_upgrade(self, db):
        cursor = db.cursor()
        cursor.execute("SELECT value FROM system WHERE name='bitten_version'")
        row = cursor.fetchone()
        if not row or int(row[0]) < schema_version:
            return True

    def upgrade_environment(self, db):
        cursor = db.cursor()
        cursor.execute("SELECT value FROM system WHERE name='bitten_version'")
        row = cursor.fetchone()
        if not row:
            self.environment_created()
        else:
            current_version = int(row[0])
            from bitten import upgrades
            for version in range(current_version + 1, schema_version + 1):
                for function in upgrades.map.get(version):
                    print textwrap.fill(inspect.getdoc(function))
                    function(self.env, db)
                    print 'Done.'
            cursor.execute("UPDATE system SET value=%s WHERE "
                           "name='bitten_version'", (schema_version,))
            self.log.info('Upgraded Bitten tables from version %d to %d',
                          current_version, schema_version)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        actions = ['BUILD_VIEW', 'BUILD_CREATE', 'BUILD_MODIFY', 'BUILD_DELETE',
                   'BUILD_EXEC']
        return actions + [('BUILD_ADMIN', actions)]

    # IWikiSyntaxProvider methods

    def get_wiki_syntax(self):
        return []

    def get_link_resolvers(self):
        def _format_link(formatter, ns, name, label):
            try:
                name = int(name)
            except ValueError:
                return label
            build = Build.fetch(self.env, name)
            if build:
                config = BuildConfig.fetch(self.env, build.config)
                title = 'Build %d ([%s] of %s) by %s' % (build.id, build.rev,
                        config.label, build.slave)
                return '<a class="build" href="%s" title="%s">%s</a>' \
                       % (formatter.href.build(build.config, build.id), title,
                          label)
            return label
        yield 'build', _format_link

    # IResourceManager methods
    
    def get_resource_realms(self):
        yield 'build'

    def get_resource_url(self, resource, href, **kwargs):
        config_name, build_id = self._parse_resource(resource.id)
        return href.build(config_name, build_id)

    def get_resource_description(self, resource, format=None, context=None,
                                 **kwargs):
        config_name, build_id = self._parse_resource(resource.id)
        config = BuildConfig.fetch(self.env, config_name)
        config_label = config.label or config_name
        if context:
            if build_id:
                return tag.a('Build %d ("%s")' % (build_id, config_label),
                        href=href.build(config_name, build_id))
            elif config_name:
                return tag.a('Build Configuration "%s"' % config_label,
                        href=href.build(config_name, build_id))
        else:
            if build_id:
                return 'Build %d ("%s")' % (build_id, config_label)
            elif config_name:
                return 'Build Configuration  "%s"' % config_label
        self.log.error("Unknown build/config resource.id: %s" % resource.id)
        return 'Unknown Build or Config'

    def _parse_resource(self, resource_id):
        """ Returns a (config_name, build_id) tuple. """
        r = resource_id.split('/', 1)
        if len(r) == 1:
            return r[0], None
        elif len(r) == 2:
            try:
                return r[0], int(r[1])
            except:
                return r[0], None
        return None, None

    # ILegacyAttachmentPolicyDelegate methods

    def check_attachment_permission(self, action, username, resource, perm):
        """ Respond to the various actions into the legacy attachment
        permissions used by the Attachment module. """
        if resource.parent.realm == 'build':
            if action == 'ATTACHMENT_VIEW':
                return 'BUILD_VIEW' in perm(resource.parent)
            elif action == 'ATTACHMENT_CREATE':
                return 'BUILD_MODIFY' in perm(resource.parent) \
                        or 'BUILD_CREATE' in perm(resource.parent)
            elif action == 'ATTACHMENT_DELETE':
                return 'BUILD_DELETE' in perm(resource.parent)
Copyright (C) 2012-2017 Edgewall Software