# -*- python -*-
# ex: set syntax=python:

# This is a sample buildmaster config file. It must be installed as
# 'master.cfg' in your buildmaster's base directory.

# This is the dictionary that the buildmaster pays attention to. We also use
# a shorter alias to save typing.
c = BuildmasterConfig = {}

####### MACROS/DSL

from buildbot.plugins import steps, util

def fetch(repo):
    return steps.Git( name             = util.Interpolate('fetch %(prop:branch)s %(prop:revision)s')
                    , mode             = 'full'
                    , method           = util.Interpolate('%(prop:fetch_method:-fresh)s')
                    , repourl          = repos[repo]['url']
                    , haltOnFailure    = True
                    , clobberOnFailure = True
                    )

def make(target, name=None, timeout=1200):
    if name is None:
        name = target[0]
    return steps.ShellCommand( name            = name
                             , description     = ''
                             , descriptionDone = ''
                             , command         = ['make'] + target
                             , haltOnFailure   = True
                             , timeout         = timeout
                             )

def shell(command, name=None):
    if name is None:
        name = command[0]
    return steps.Compile( name            = name
                        , description     = ''
                        , descriptionDone = ''
                        , command         = command
                        )

def needsRebasedOn(branch):
    return steps.ShellCommand( name            = 'branch based on: ' + branch
                             , description     = ''
                             , descriptionDone = ''
                             , command         = ['git', 'rebased-on', branch]
                             , haltOnFailure   = True
                             )

def gitBisect(command):
    return steps.ShellCommand( name            = 'bisect ' + command[0]
                             , description     = ''
                             , descriptionDone = ''
                             , command         = ['git', 'bisect'] + command
                             , alwaysRun       = True
                             )

def flatten(lls):
    return [ i for ls in lls
               for i  in ls ]

####### REPOSITORIES

repos = { '{###TEST_NAME###}' : { 'url': '{###REPOSITORY-ULR###}'
                                , 'steps':   [ needsRebasedOn('origin/master')
                                             , make(['clean'])
                                             , make(['deps'])
                                             , make(['build'])
                                             , make(['test', '-j6'])
                                             ]
                                , 'delay':   10
                                , 'worker':  '{###WORKER-NAME###}'
                                , 'project': '{###PROJECT-NAME###}'
                                , 'token':   '{###GITHUB-AUTH-TOKEN###}'
                                ## OPTIONAL
                                # , 'bisect':  { 'good':    'origin/master'
                                #              , 'failing': [ './command' , 'to-list' , 'a-failing-test' ]
                                #              , 'init':    [ make(['clean']) ]
                                #              , 'test':    [ 'bash', '-c', util.Interpolate('make clean && make deps && make build && make %(prop:failing_test)s.test') ]
                                #              }
                                # }
                                }
        }

####### WORKERS

# The 'workers' list defines the set of recognized buildworkers. Each element is
# a Worker object, specifying a unique worker name and password.  The same
# workername and password must be configured on the worker.

from buildbot.plugins import worker

c['workers'] = [ worker.Worker("{###WORKER-NAME###}", "{###WORKER-PASSWORD###}", max_builds={###WORKER-MAX-SIMULTANEOUS-BUILDS###})
               ]

# 'protocols' contains information about protocols which master will use for
# communicating with workers.
# You must define at least 'port' option that workers could connect to your master
# with this protocol.
# 'port' must match the value configured into the buildworkers (with their
# --master option)
c['protocols'] = {'pb': {'port': 9989}}

####### CHANGESOURCES

# the 'change_source' setting tells the buildmaster how it should find out
# about source code changes.

####### SCHEDULERS

# Configure the Schedulers, which decide how to react to incoming changes.

from buildbot.plugins           import schedulers
from buildbot.schedulers.filter import ChangeFilter

c['schedulers'] = flatten([
        ## SCHEDULER FOR PULL REQUESTS
        [ schedulers.AnyBranchScheduler( name            = repo
                                       , builderNames    = [repo]
                                       , change_filter   = ChangeFilter(project = repos[repo]['project'])
                                       , treeStableTimer = repos[repo]['delay'] + 5
                                       )

        ## SCHEDULER FOR TRIGGERING MANUAL BUILDS VIA WEB INTERFACE
        , schedulers.ForceScheduler( name         = repo + "_force"
                                   , builderNames = [repo]
                                   , properties   = [ util.ChoiceStringParameter( name    = 'fetch_method'
                                                                                , label   = 'Fetch method'
                                                                                , choices = [ 'fresh' , 'clobber' ]
                                                                                , default = 'fresh'
                                                                                )
                                                    ]
                                   )
        ]
    for repo in repos

        ## SCHEDULER FOR TRIGGERING BISECT ON REPOS WHICH HAVE IT
    ]) + [ schedulers.Triggerable( name         = repo + '_bisect'
                                 , builderNames = [repo + '-bisect']
                                 )
           for repo in repos if 'bisect' in repos[repo]
         ]

####### BUILDERS

# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
# what steps, and which workers can execute them.  Note that any particular build will
# only take place on one worker.

def triggerBisectSteps(repo):
    return [ steps.SetPropertyFromCommand( name            = 'checking for failures'
                                         , command         = repos[repo]['bisect']['failing']
                                         , property        = 'failing_test'
                                         , alwaysRun       = True
                                         )
           , steps.Trigger( name            = 'trigger bisect'
                          , schedulerNames  = [repo + '_bisect']
                          , doStepIf        = lambda step: step.getProperty('failing_test') != ''
                          , waitForFinish   = False
                          , set_properties  = { 'failing_test' : util.Interpolate('%(prop:failing_test)s') }
                          , alwaysRun       = True
                          )
           ]

def defaultBuildFactory(repo):
    factory_steps = ( [ fetch(repo) ]
                    + repos[repo]['steps']
                    )
    if 'bisect' in repos[repo]:
        factory_steps += triggerBisectSteps(repo)
    return util.BuildFactory(factory_steps)

def bisectBuildFactory(repo):
    return util.BuildFactory( [ fetch(repo) ]
                            + repos[repo]['bisect']['init']
                            + [ gitBisect(['start', 'HEAD', repos[repo]['bisect']['good']])
                              , gitBisect(['log'])
                              , gitBisect(['run'] + repos[repo]['bisect']['test'])
                              ]
                            )

c['builders'] = flatten([
        ## MAIN TESTER FOR REPO
        [ util.BuilderConfig( name        = repo
                            , workernames = repos[repo]['worker']
                            , factory     = defaultBuildFactory(repo)
                            )
        ]
    for repo in repos ]) + [

        ## BISECT TESTER FOR REPO
          util.BuilderConfig( name        = repo + "-bisect"
                            , workernames = repos[repo]['worker']
                            , factory     = bisectBuildFactory(repo)
                            )
    for repo in repos if 'bisect' in repos[repo]
        ]

####### WEBSITE CONFIGURATION

c['www'] = { 'port'    : 8010
           , 'plugins' : { 'waterfall_view' : True
                         , 'grid_view'      : True
                         , 'console_view'   : True
                         }
           , 'change_hook_dialects' : { 'github' : { 'secret' : '{###BUILDBOT_SECRET_KEY###}'
                                                   , 'strict' : True
                                                   }
                                      }
           , 'auth'  : util.GitHubAuth('{###GITHUB_AUTH_CLIENT###}', '{###GITHUB_AUTH_SECRET###}')
           , 'authz' : util.Authz( roleMatchers = [ util.RolesFromGroups() ]
                                 , allowRules   = [ util.AnyControlEndpointMatcher(role = "{###GITHUB_ORGANIZATION###}")
                                                  ]
                                 )
           }

####### REPORT TARGETS

from buildbot.plugins import reporters

c['services'] = [
    reporters.GitHubStatusPush( name             = ('GitHubStatus_' + repo)
                              , token            = repos[repo]['token']
                              , context          = util.Interpolate("buildbot/%(prop:buildername)s")
                              , startDescription = 'Build started.'
                              , endDescription   = 'Build done.'
                              , builders         =  [repo]
                              )
    for repo in repos if 'token' in repos[repo]
]

####### PROJECT IDENTITY

# the 'title' string will appear at the top of this buildbot
# installation's html.WebStatus home page (linked to the
# 'titleURL') and is embedded in the title of the waterfall HTML page.

c['title'] = "{###ORGANIZATION_NAME###}"
c['titleURL'] = "{###ORGANIZATION_URL###}"
c['buildbotURL'] = "{###CI_SERVER_URL###}"

####### USAGE REPORTS

# Only report basic information to buildbot.net: versions, platform and plugins used.

c['buildbotNetUsageData'] = 'basic'

####### DB URL

c['db'] = {
    # This specifies what database buildbot uses to store its state.  You can leave
    # this at its default for all but the largest installations.
    'db_url' : "sqlite:///state.sqlite",
}
