1
# Copyright 2004-2005 Canonical Ltd. All rights reserved.
3
"""Fill the database with information from a Bazaar branch."""
7
__all__ = ['bzrScanBuilders']
11
from datetime import datetime
13
from buildbot.process.base import ConfigurableBuildFactory, ConfigurableBuild
14
from buildbot.process.step import ShellCommand
16
from canonical.launchpad.database import Branch
17
from canonical.database.constants import UTC_NOW
19
from importd.util import (
20
getTxnManager, tryToAbortTransaction, NotifyingBuild)
23
def bzrScanBuilders(slavenames, runner_path=None, periodic=None):
25
getTxnManager().begin()
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')
37
runner_path = str(runner_path)
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,
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()
51
# Safely abort the transaction and re-raise the exception.
52
tryToAbortTransaction()
57
class BzrScanBuildFactory(ConfigurableBuildFactory):
58
"""Factory for Buildbot Build objects that run bzrsync."""
62
"""Type used by inherited newBuild to instanciate a Build object."""
65
def __init__(self, scanjob, runner_path):
67
self.scanjob = scanjob
68
self.runner_path = runner_path
72
"""Add a single build step that runs bzrsync in a process.
74
The bzrsync process will be killed if it produces no output during the
75
number of seconds specified as 'timeout'.
77
command = [sys.executable, self.runner_path, str(self.scanjob['id'])]
78
self.steps.append((BzrScanShellCommand, {
81
'branch_name': self.scanjob['branch_name'],
85
"""Make a new Build instance.
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
94
class BzrScanShellCommand(ShellCommand):
95
"""Shell command with custom short display."""
97
def __init__(self, branch_name, **kwargs):
98
ShellCommand.__init__(self, **kwargs)
99
self.branch_name = branch_name
102
"""Short display of BzrScan steps in buildbot."""
103
# Buildbot requires this method to return a sequence of words.
104
return [self.branch_name]
107
class BzrScanBuild(NotifyingBuild):
108
"""Build that notify a BzrScanBuildObserver of build starts and stops."""
110
def getObserver(self):
111
"""Object to send notifications to.
113
startBuild is called before the build starts.
115
buildFinished is called after the build complete, with a boolean
116
argument indicating whether the build was successful.
118
return BzrScanBuildObserver(self)
121
class BzrScanBuildObserver:
122
"""Update the database when a Bazaar sync build starts or finish."""
124
def __init__(self, build):
126
scanjob = build.scanjob
127
self.branch_id = scanjob['id']
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()
136
def buildFinished(self, successful):
137
"""Called after a sync finishes.
139
The 'successful' parameter is false if the sync process was not
142
getTxnManager().begin()
143
self.setLastMirrorAttempt()
144
getTxnManager().commit()
146
def setLastMirrorAttempt(self):
147
"""Set the last_mirror_attempt timestamp of the synced branch."""
148
self.getBranch().last_mirror_attempt = UTC_NOW
151
"""The database Branch object associated to this sync build."""
152
return Branch.get(self.branch_id)