~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/importd/bzrscan.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2005-11-18 05:11:00 UTC
  • mfrom: (1102.1.159 launchpad-branches)
  • Revision ID: pqm@pqm.ubuntu.com-20051118051100-b3c97d86dfa45faf
[r=SteveA] Merging bzr branch support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2004-2005 Canonical Ltd.  All rights reserved.
 
2
 
 
3
"""Fill the database with information from a Bazaar branch."""
 
4
 
 
5
__metaclass__ = type
 
6
 
 
7
__all__ = ['bzrScanBuilders']
 
8
 
 
9
import sys
 
10
import os
 
11
from datetime import datetime
 
12
 
 
13
from buildbot.process.base import ConfigurableBuildFactory, ConfigurableBuild
 
14
from buildbot.process.step import ShellCommand
 
15
 
 
16
from canonical.launchpad.database import Branch
 
17
from canonical.database.constants import UTC_NOW
 
18
 
 
19
from importd.util import (
 
20
    getTxnManager, tryToAbortTransaction, NotifyingBuild)
 
21
 
 
22
 
 
23
def bzrScanBuilders(slavenames, runner_path=None, periodic=None):
 
24
    builders = []
 
25
    getTxnManager().begin()
 
26
    try:
 
27
        branches = list(Branch.select())
 
28
        branches.sort(key=lambda branch: branch.id)
 
29
        for branch in branches:
 
30
            name = 'branch-%03d' % (branch.id,)
 
31
            slavename = slavenames[hash(name) % len(slavenames)]
 
32
            builddir = 'bzrscan-jobs'
 
33
            if runner_path is None:
 
34
                runner_path = os.path.join(
 
35
                    os.path.dirname(__file__), 'bzrsync.py')
 
36
            else:
 
37
                runner_path = str(runner_path)
 
38
            if periodic is None:
 
39
                periodic = 24 * 60 * 60 # one day in seconds
 
40
            branch_name = '%s %s %s' % (
 
41
                branch.owner.name, branch.product_name, branch.name)
 
42
            scanjob = {'id': branch.id, 'branch_name': branch_name,
 
43
                       'interval': periodic}
 
44
            factory = BzrScanBuildFactory(scanjob, runner_path)
 
45
            builderDict = {'name': name, 'slavename': slavename,
 
46
                           'builddir': builddir, 'factory': factory,
 
47
                           'periodicBuildTime': periodic}
 
48
            builders.append(builderDict)
 
49
        getTxnManager().abort()
 
50
    except:
 
51
        # Safely abort the transaction and re-raise the exception. 
 
52
        tryToAbortTransaction()
 
53
        raise
 
54
    return builders
 
55
 
 
56
 
 
57
class BzrScanBuildFactory(ConfigurableBuildFactory):
 
58
    """Factory for Buildbot Build objects that run bzrsync."""
 
59
 
 
60
    @property
 
61
    def buildClass(self):
 
62
        """Type used by inherited newBuild to instanciate a Build object."""
 
63
        return BzrScanBuild
 
64
 
 
65
    def __init__(self, scanjob, runner_path):
 
66
        self.steps = []
 
67
        self.scanjob = scanjob
 
68
        self.runner_path = runner_path
 
69
        self.addSteps()
 
70
 
 
71
    def addSteps(self):
 
72
        """Add a single build step that runs bzrsync in a process.
 
73
 
 
74
        The bzrsync process will be killed if it produces no output during the
 
75
        number of seconds specified as 'timeout'.
 
76
        """
 
77
        command = [sys.executable, self.runner_path, str(self.scanjob['id'])]
 
78
        self.steps.append((BzrScanShellCommand, {
 
79
            'timeout': 1200,
 
80
            'workdir': None,
 
81
            'branch_name': self.scanjob['branch_name'],
 
82
            'command': command}))
 
83
 
 
84
    def newBuild(self):
 
85
        """Make a new Build instance.
 
86
 
 
87
        Pass in job information in scanjob to allow the Build to update the
 
88
        database with status information."""
 
89
        build = ConfigurableBuildFactory.newBuild(self)
 
90
        build.scanjob = self.scanjob
 
91
        return build
 
92
 
 
93
 
 
94
class BzrScanShellCommand(ShellCommand):
 
95
    """Shell command with custom short display."""
 
96
 
 
97
    def __init__(self, branch_name, **kwargs):
 
98
        ShellCommand.__init__(self, **kwargs)
 
99
        self.branch_name = branch_name
 
100
 
 
101
    def words(self):
 
102
        """Short display of BzrScan steps in buildbot."""
 
103
        # Buildbot requires this method to return a sequence of words.
 
104
        return [self.branch_name]
 
105
 
 
106
 
 
107
class BzrScanBuild(NotifyingBuild):
 
108
    """Build that notify a BzrScanBuildObserver of build starts and stops."""
 
109
 
 
110
    def getObserver(self):
 
111
        """Object to send notifications to.
 
112
 
 
113
        startBuild is called before the build starts.
 
114
        
 
115
        buildFinished is called after the build complete, with a boolean
 
116
        argument indicating whether the build was successful.
 
117
        """
 
118
        return BzrScanBuildObserver(self)
 
119
 
 
120
 
 
121
class BzrScanBuildObserver:
 
122
    """Update the database when a Bazaar sync build starts or finish."""
 
123
 
 
124
    def __init__(self, build):
 
125
        self.build = build
 
126
        scanjob = build.scanjob
 
127
        self.branch_id = scanjob['id']
 
128
 
 
129
    def startBuild(self):
 
130
        """Called before a sync starts."""
 
131
        getTxnManager().begin()
 
132
        # Do nothing. When this method exits it's required that the current
 
133
        # process has started a transaction at least once.
 
134
        getTxnManager().commit()
 
135
 
 
136
    def buildFinished(self, successful):
 
137
        """Called after a sync finishes.
 
138
 
 
139
        The 'successful' parameter is false if the sync process was not
 
140
        successful.
 
141
        """
 
142
        getTxnManager().begin()
 
143
        self.setLastMirrorAttempt()
 
144
        getTxnManager().commit()
 
145
 
 
146
    def setLastMirrorAttempt(self):
 
147
        """Set the last_mirror_attempt timestamp of the synced branch."""
 
148
        self.getBranch().last_mirror_attempt = UTC_NOW
 
149
 
 
150
    def getBranch(self):
 
151
        """The database Branch object associated to this sync build."""
 
152
        return Branch.get(self.branch_id)