2 # ex: set syntax=python:
10 from dateutil.tz import tzutc
11 from datetime import datetime, timedelta
13 from twisted.internet import defer
14 from twisted.python import log
16 from buildbot import locks
17 from buildbot.data import resultspec
18 from buildbot.changes import filter
19 from buildbot.changes.gitpoller import GitPoller
20 from buildbot.config import BuilderConfig
21 from buildbot.plugins import reporters
22 from buildbot.plugins import schedulers
23 from buildbot.plugins import steps
24 from buildbot.plugins import util
25 from buildbot.process import properties
26 from buildbot.process import results
27 from buildbot.process.factory import BuildFactory
28 from buildbot.process.properties import Interpolate
29 from buildbot.process.properties import Property
30 from buildbot.schedulers.basic import SingleBranchScheduler
31 from buildbot.schedulers.forcesched import BaseParameter
32 from buildbot.schedulers.forcesched import ForceScheduler
33 from buildbot.schedulers.forcesched import ValidationError
34 from buildbot.steps.master import MasterShellCommand
35 from buildbot.steps.shell import SetPropertyFromCommand
36 from buildbot.steps.shell import ShellCommand
37 from buildbot.steps.source.git import Git
38 from buildbot.steps.transfer import FileDownload
39 from buildbot.steps.transfer import FileUpload
40 from buildbot.steps.transfer import StringDownload
41 from buildbot.worker import Worker
44 if not os.path.exists("twistd.pid"):
45 with open("twistd.pid", "w") as pidfile:
46 pidfile.write("{}".format(os.getpid()))
48 # This is a sample buildmaster config file. It must be installed as
49 # 'master.cfg' in your buildmaster's base directory.
51 ini = configparser.ConfigParser()
52 ini.read(os.getenv("BUILDMASTER_CONFIG", "./config.ini"))
54 if "general" not in ini or "phase1" not in ini or "rsync" not in ini:
55 raise ValueError("Fix your configuration")
60 work_dir = os.path.abspath(ini['general'].get("workdir", "."))
61 scripts_dir = os.path.abspath("../scripts")
63 config_seed = inip1.get("config_seed", "")
65 repo_url = ini['repo'].get("url")
66 repo_branch = ini['repo'].get("branch", "master")
68 rsync_bin_url = ini['rsync'].get("binary_url")
69 rsync_bin_key = ini['rsync'].get("binary_password")
70 rsync_bin_defopts = ["-v", "-4", "--timeout=120"]
72 if rsync_bin_url.find("::") > 0 or rsync_bin_url.find("rsync://") == 0:
73 rsync_bin_defopts += ["--contimeout=20"]
75 rsync_src_url = ini['rsync'].get("source_url")
76 rsync_src_key = ini['rsync'].get("source_password")
77 rsync_src_defopts = ["-v", "-4", "--timeout=120"]
79 if rsync_src_url.find("::") > 0 or rsync_src_url.find("rsync://") == 0:
80 rsync_src_defopts += ["--contimeout=20"]
83 usign_comment = "untrusted comment: " + repo_branch.replace("-", " ").title() + " key"
85 if ini.has_section("usign"):
86 usign_key = ini['usign'].get("key")
87 usign_comment = ini['usign'].get("comment", usign_comment)
89 enable_kmod_archive = inip1.getboolean("kmod_archive", False)
91 # PB port can be either a numeric port or a connection string
92 pb_port = inip1.get("port") or 9989
94 # This is the dictionary that the buildmaster pays attention to. We also use
95 # a shorter alias to save typing.
96 c = BuildmasterConfig = {}
98 ####### PROJECT IDENTITY
100 # the 'title' string will appear at the top of this buildbot
101 # installation's html.WebStatus home page (linked to the
102 # 'titleURL') and is embedded in the title of the waterfall HTML page.
104 c['title'] = ini['general'].get("title")
105 c['titleURL'] = ini['general'].get("title_url")
107 # the 'buildbotURL' string should point to the location where the buildbot's
108 # internal web server (usually the html.WebStatus page) is visible. This
109 # typically uses the port number set in the Waterfall 'status' entry, but
110 # with an externally-visible host name which the buildbot cannot figure out
113 c['buildbotURL'] = inip1.get("buildbot_url")
117 # The 'workers' list defines the set of recognized buildworkers. Each element is
118 # a Worker object, specifying a unique worker name and password. The same
119 # worker name and password must be configured on the worker.
124 for section in ini.sections():
125 if section.startswith("worker "):
126 if ini.has_option(section, "name") and ini.has_option(section, "password") and \
127 (not ini.has_option(section, "phase") or ini.getint(section, "phase") == 1):
128 sl_props = { 'dl_lock':None, 'ul_lock':None }
129 name = ini.get(section, "name")
130 password = ini.get(section, "password")
131 if ini.has_option(section, "dl_lock"):
132 lockname = ini.get(section, "dl_lock")
133 sl_props['dl_lock'] = lockname
134 if lockname not in NetLocks:
135 NetLocks[lockname] = locks.MasterLock(lockname)
136 if ini.has_option(section, "ul_lock"):
137 lockname = ini.get(section, "ul_lock")
138 sl_props['ul_lock'] = lockname
139 if lockname not in NetLocks:
140 NetLocks[lockname] = locks.MasterLock(lockname)
141 c['workers'].append(Worker(name, password, max_builds = 1, properties = sl_props))
143 c['protocols'] = {'pb': {'port': pb_port}}
146 c['collapseRequests'] = True
148 # Reduce amount of backlog data
149 c['configurators'] = [util.JanitorConfigurator(
150 logHorizon=timedelta(days=3),
154 @defer.inlineCallbacks
155 def getNewestCompleteTime(bldr):
156 """Returns the complete_at of the latest completed and not SKIPPED
157 build request for this builder, or None if there are no such build
158 requests. We need to filter out SKIPPED requests because we're
159 using collapseRequests=True which is unfortunately marking all
160 previous requests as complete when new buildset is created.
162 @returns: datetime instance or None, via Deferred
165 bldrid = yield bldr.getBuilderId()
166 completed = yield bldr.master.data.get(
167 ('builders', bldrid, 'buildrequests'),
169 resultspec.Filter('complete', 'eq', [True]),
170 resultspec.Filter('results', 'ne', [results.SKIPPED]),
172 order=['-complete_at'], limit=1)
176 complete_at = completed[0]['complete_at']
178 last_build = yield bldr.master.data.get(
181 resultspec.Filter('builderid', 'eq', [bldrid]),
183 order=['-started_at'], limit=1)
185 if last_build and last_build[0]:
186 last_complete_at = last_build[0]['complete_at']
187 if last_complete_at and (last_complete_at > complete_at):
188 return last_complete_at
192 @defer.inlineCallbacks
193 def prioritizeBuilders(master, builders):
194 """Returns sorted list of builders by their last timestamp of completed and
197 @returns: list of sorted builders
200 def is_building(bldr):
201 return bool(bldr.building) or bool(bldr.old_building)
204 d = defer.maybeDeferred(getNewestCompleteTime, bldr)
205 d.addCallback(lambda complete_at: (complete_at, bldr))
209 (complete_at, bldr) = item
213 complete_at = date.replace(tzinfo=tzutc())
215 if is_building(bldr):
217 complete_at = date.replace(tzinfo=tzutc())
219 return (complete_at, bldr.name)
221 results = yield defer.gatherResults([bldr_info(bldr) for bldr in builders])
222 results.sort(key=bldr_sort)
225 log.msg("prioritizeBuilders: {:>20} complete_at: {}".format(r[1].name, r[0]))
227 return [r[1] for r in results]
229 c['prioritizeBuilders'] = prioritizeBuilders
231 ####### CHANGESOURCES
237 def populateTargets():
238 sourcegit = work_dir + '/source.git'
239 if os.path.isdir(sourcegit):
240 subprocess.call(["rm", "-rf", sourcegit])
242 subprocess.call(["git", "clone", "--depth=1", "--branch="+repo_branch, repo_url, sourcegit])
244 os.makedirs(sourcegit + '/tmp', exist_ok=True)
245 findtargets = subprocess.Popen(['./scripts/dump-target-info.pl', 'targets'],
246 stdout = subprocess.PIPE, stderr = subprocess.DEVNULL, cwd = sourcegit)
249 line = findtargets.stdout.readline()
252 ta = line.decode().strip().split(' ')
253 targets.append(ta[0])
255 subprocess.call(["rm", "-rf", sourcegit])
259 # the 'change_source' setting tells the buildmaster how it should find out
260 # about source code changes. Here we point to the buildbot clone of pyflakes.
262 c['change_source'] = []
263 c['change_source'].append(GitPoller(
265 workdir=work_dir+'/work.git', branch=repo_branch,
270 # Configure the Schedulers, which decide how to react to incoming changes. In this
271 # case, just kick off a 'basebuild' build
273 class TagChoiceParameter(BaseParameter):
274 spec_attributes = ["strict", "choices"]
278 def __init__(self, name, label=None, **kw):
279 super().__init__(name, label, **kw)
280 self._choice_list = []
285 basever = re.search(r'-([0-9]+\.[0-9]+)$', repo_branch)
288 findtags = subprocess.Popen(
289 ['git', 'ls-remote', '--tags', repo_url],
290 stdout = subprocess.PIPE)
293 line = findtags.stdout.readline()
298 tagver = re.search(r'\brefs/tags/v([0-9]+\.[0-9]+\.[0-9]+(?:-rc[0-9]+)?)$', line.decode().strip())
300 if tagver and tagver[1].find(basever[1]) == 0:
301 taglist.append(tagver[1])
303 taglist.sort(reverse=True, key=lambda tag: tag if re.search(r'-rc[0-9]+$', tag) else tag + '-z')
304 taglist.insert(0, '')
306 self._choice_list = taglist
308 return self._choice_list
310 def parse_from_arg(self, s):
311 if self.strict and s not in self._choice_list:
312 raise ValidationError("'%s' does not belong to list of available choices '%s'" % (s, self._choice_list))
316 c['schedulers'].append(SingleBranchScheduler(
318 change_filter = filter.ChangeFilter(branch=repo_branch),
319 treeStableTimer = 60,
320 builderNames = targets))
322 c['schedulers'].append(ForceScheduler(
324 buttonName = "Force builds",
325 label = "Force build details",
326 builderNames = [ "00_force_build" ],
329 util.CodebaseParameter(
331 label = "Repository",
332 branch = util.FixedParameter(name = "branch", default = ""),
333 revision = util.FixedParameter(name = "revision", default = ""),
334 repository = util.FixedParameter(name = "repository", default = ""),
335 project = util.FixedParameter(name = "project", default = "")
339 reason = util.StringParameter(
342 default = "Trigger build",
348 util.NestedParameter(
350 label="Build Options",
353 util.ChoiceStringParameter(
355 label = "Build target",
357 choices = [ "all" ] + targets
371 # The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
372 # what steps, and which workers can execute them. Note that any particular build will
373 # only take place on one worker.
375 def IsTaggingRequested(step):
376 val = step.getProperty("tag")
377 if val and re.match(r"^[0-9]+\.[0-9]+\.[0-9]+(?:-rc[0-9]+)?$", val):
382 def IsNoMasterBuild(step):
383 return repo_branch != "master"
385 def GetBaseVersion():
386 if re.match(r"^[^-]+-[0-9]+\.[0-9]+$", repo_branch):
387 return repo_branch.split('-')[1]
392 def GetVersionPrefix(props):
393 basever = GetBaseVersion()
394 if props.hasProperty("tag") and re.match(r"^[0-9]+\.[0-9]+\.[0-9]+(?:-rc[0-9]+)?$", props["tag"]):
395 return "%s/" % props["tag"]
396 elif basever != "master":
397 return "%s-SNAPSHOT/" % basever
401 def GetNextBuild(builder, requests):
403 if r.properties and r.properties.hasProperty("tag"):
407 log.msg("GetNextBuild: {:>20} id: {} bsid: {}".format(builder.name, r.id, r.bsid))
410 def MakeEnv(overrides=None, tryccache=False):
412 'CCC': Interpolate("%(prop:cc_command:-gcc)s"),
413 'CCXX': Interpolate("%(prop:cxx_command:-g++)s"),
416 env['CC'] = Interpolate("%(prop:builddir)s/ccache_cc.sh")
417 env['CXX'] = Interpolate("%(prop:builddir)s/ccache_cxx.sh")
418 env['CCACHE'] = Interpolate("%(prop:ccache_command:-)s")
420 env['CC'] = env['CCC']
421 env['CXX'] = env['CCXX']
423 if overrides is not None:
424 env.update(overrides)
428 def NetLockDl(props):
430 if props.hasProperty("dl_lock"):
431 lock = NetLocks[props["dl_lock"]]
433 return [lock.access('exclusive')]
438 def NetLockUl(props):
440 if props.hasProperty("ul_lock"):
441 lock = NetLocks[props["ul_lock"]]
443 return [lock.access('exclusive')]
448 def TagPropertyValue(props):
449 if props.hasProperty("options"):
450 options = props.getProperty("options")
451 if type(options) is dict:
452 return options.get("tag")
455 def IsTargetSelected(target):
456 def CheckTargetProperty(step):
458 options = step.getProperty("options")
459 if type(options) is dict:
460 selected_target = options.get("target", "all")
461 if selected_target != "all" and selected_target != target:
468 return CheckTargetProperty
470 def UsignSec2Pub(seckey, comment="untrusted comment: secret key"):
472 seckey = base64.b64decode(seckey)
476 return "{}\n{}".format(re.sub(r"\bsecret key$", "public key", comment),
477 base64.b64encode(seckey[0:2] + seckey[32:40] + seckey[72:]))
482 dlLock = locks.WorkerLock("worker_dl")
486 for worker in c['workers']:
487 workerNames.append(worker.workername)
489 force_factory = BuildFactory()
491 c['builders'].append(BuilderConfig(
492 name = "00_force_build",
493 workernames = workerNames,
494 factory = force_factory))
496 for target in targets:
497 ts = target.split('/')
499 factory = BuildFactory()
501 # setup shared work directory if required
502 factory.addStep(ShellCommand(
504 description = "Setting up shared work directory",
505 command = 'test -L "$PWD" || (mkdir -p ../shared-workdir && rm -rf "$PWD" && ln -s shared-workdir "$PWD")',
507 haltOnFailure = True))
509 # find number of cores
510 factory.addStep(SetPropertyFromCommand(
513 description = "Finding number of CPUs",
514 command = ["nproc"]))
516 # find gcc and g++ compilers
517 factory.addStep(FileDownload(
518 name = "dlfindbinpl",
519 mastersrc = scripts_dir + '/findbin.pl',
520 workerdest = "../findbin.pl",
523 factory.addStep(SetPropertyFromCommand(
525 property = "cc_command",
526 description = "Finding gcc command",
528 "../findbin.pl", "gcc", "", "",
530 haltOnFailure = True))
532 factory.addStep(SetPropertyFromCommand(
534 property = "cxx_command",
535 description = "Finding g++ command",
537 "../findbin.pl", "g++", "", "",
539 haltOnFailure = True))
541 # see if ccache is available
542 factory.addStep(SetPropertyFromCommand(
543 property = "ccache_command",
544 command = ["which", "ccache"],
545 description = "Testing for ccache command",
546 haltOnFailure = False,
547 flunkOnFailure = False,
548 warnOnFailure = False,
551 # Workaround bug when switching from a checked out tag back to a branch
552 # Ref: http://lists.infradead.org/pipermail/openwrt-devel/2019-June/017809.html
553 factory.addStep(ShellCommand(
554 name = "gitcheckout",
555 description = "Ensure that Git HEAD is sane",
556 command = "if [ -d .git ]; then git checkout -f %s && git branch --set-upstream-to origin/%s || rm -fr .git; else exit 0; fi" %(repo_branch, repo_branch),
557 haltOnFailure = True))
559 # check out the source
561 # if repo doesn't exist: 'git clone repourl'
562 # method 'clean' runs 'git clean -d -f', method fresh runs 'git clean -d -f x'. Only works with mode='full'
563 # 'git fetch -t repourl branch; git reset --hard revision'
567 branch = repo_branch,
571 haltOnFailure = True,
575 factory.addStep(ShellCommand(
577 description = "Fetching Git remote refs",
578 command = ["git", "fetch", "origin", "+refs/heads/%s:refs/remotes/origin/%s" %(repo_branch, repo_branch)],
583 factory.addStep(ShellCommand(
585 description = "Checking out Git tag",
586 command = ["git", "checkout", Interpolate("tags/v%(prop:tag:-)s")],
587 haltOnFailure = True,
588 doStepIf = IsTaggingRequested
591 # Verify that Git HEAD points to a tag or branch
592 # Ref: http://lists.infradead.org/pipermail/openwrt-devel/2019-June/017809.html
593 factory.addStep(ShellCommand(
595 description = "Ensure that Git HEAD is pointing to a branch or tag",
596 command = 'git rev-parse --abbrev-ref HEAD | grep -vxqF HEAD || git show-ref --tags --dereference 2>/dev/null | sed -ne "/^$(git rev-parse HEAD) / { s|^.*/||; s|\\^.*||; p }" | grep -qE "^v[0-9][0-9]\\."',
597 haltOnFailure = True))
599 factory.addStep(ShellCommand(
601 description = "Remove tmp folder",
602 command=["rm", "-rf", "tmp/"]))
605 factory.addStep(ShellCommand(
606 name = "rmfeedlinks",
607 description = "Remove feed symlinks",
608 command=["rm", "-rf", "package/feeds/"]))
610 factory.addStep(StringDownload(
612 s = '#!/bin/sh\nexec ${CCACHE} ${CCC} "$@"\n',
613 workerdest = "../ccache_cc.sh",
617 factory.addStep(StringDownload(
619 s = '#!/bin/sh\nexec ${CCACHE} ${CCXX} "$@"\n',
620 workerdest = "../ccache_cxx.sh",
625 factory.addStep(ShellCommand(
626 name = "updatefeeds",
627 description = "Updating feeds",
628 command=["./scripts/feeds", "update"],
629 env = MakeEnv(tryccache=True),
630 haltOnFailure = True,
635 factory.addStep(ShellCommand(
636 name = "installfeeds",
637 description = "Installing feeds",
638 command=["./scripts/feeds", "install", "-a"],
639 env = MakeEnv(tryccache=True),
644 if config_seed is not None:
645 factory.addStep(StringDownload(
646 name = "dlconfigseed",
647 s = config_seed + '\n',
648 workerdest = ".config",
653 factory.addStep(ShellCommand(
655 description = "Seeding .config",
656 command = "printf 'CONFIG_TARGET_%s=y\\nCONFIG_TARGET_%s_%s=y\\nCONFIG_SIGNED_PACKAGES=%s\\n' >> .config" %(ts[0], ts[0], ts[1], 'y' if usign_key is not None else 'n')
659 factory.addStep(ShellCommand(
661 description = "Removing output directory",
662 command = ["rm", "-rf", "bin/"]
665 factory.addStep(ShellCommand(
667 description = "Populating .config",
668 command = ["make", "defconfig"],
673 factory.addStep(ShellCommand(
675 description = "Checking architecture",
676 command = ["grep", "-sq", "CONFIG_TARGET_%s=y" %(ts[0]), ".config"],
684 factory.addStep(SetPropertyFromCommand(
687 description = "Finding libc suffix",
688 command = ["sed", "-ne", '/^CONFIG_LIBC=/ { s!^CONFIG_LIBC="\\(.*\\)"!\\1!; s!^musl$!!; s!.\\+!-&!p }', ".config"]))
691 if usign_key is not None:
692 factory.addStep(StringDownload(
693 name = "dlkeybuildpub",
694 s = UsignSec2Pub(usign_key, usign_comment),
695 workerdest = "key-build.pub",
699 factory.addStep(StringDownload(
701 s = "# fake private key",
702 workerdest = "key-build",
706 factory.addStep(StringDownload(
707 name = "dlkeybuilducert",
708 s = "# fake certificate",
709 workerdest = "key-build.ucert",
714 factory.addStep(ShellCommand(
716 description = "Preparing dl/",
717 command = "mkdir -p $HOME/dl && rm -rf ./dl && ln -sf $HOME/dl ./dl",
723 factory.addStep(ShellCommand(
725 description = "Building and installing GNU tar",
726 command = ["make", Interpolate("-j%(prop:nproc:-1)s"), "tools/tar/compile", "V=s"],
727 env = MakeEnv(tryccache=True),
732 factory.addStep(ShellCommand(
734 description = "Populating dl/",
735 command = ["make", Interpolate("-j%(prop:nproc:-1)s"), "download", "V=s"],
738 locks = properties.FlattenList(NetLockDl, [dlLock.access('exclusive')]),
741 factory.addStep(ShellCommand(
743 description = "Cleaning base-files",
744 command=["make", "package/base-files/clean", "V=s"]
748 factory.addStep(ShellCommand(
750 description = "Building and installing tools",
751 command = ["make", Interpolate("-j%(prop:nproc:-1)s"), "tools/install", "V=s"],
752 env = MakeEnv(tryccache=True),
756 factory.addStep(ShellCommand(
758 description = "Building and installing toolchain",
759 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "toolchain/install", "V=s"],
764 factory.addStep(ShellCommand(
766 description = "Building kmods",
767 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "target/compile", "V=s", "IGNORE_ERRORS=n m", "BUILD_LOG=1"],
772 # find kernel version
773 factory.addStep(SetPropertyFromCommand(
774 name = "kernelversion",
775 property = "kernelversion",
776 description = "Finding the effective Kernel version",
777 command = "make --no-print-directory -C target/linux/ val.LINUX_VERSION val.LINUX_RELEASE val.LINUX_VERMAGIC | xargs printf '%s-%s-%s\\n'",
778 env = { 'TOPDIR': Interpolate("%(prop:builddir)s/build") }
781 factory.addStep(ShellCommand(
783 description = "Cleaning up package build",
784 command=["make", "package/cleanup", "V=s"]
787 factory.addStep(ShellCommand(
789 description = "Building packages",
790 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "package/compile", "V=s", "IGNORE_ERRORS=n m", "BUILD_LOG=1"],
795 factory.addStep(ShellCommand(
797 description = "Installing packages",
798 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "package/install", "V=s"],
803 factory.addStep(ShellCommand(
805 description = "Indexing packages",
806 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "package/index", "V=s", "CONFIG_SIGNED_PACKAGES="],
811 factory.addStep(ShellCommand(
813 description = "Building and installing images",
814 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "target/install", "V=s"],
819 factory.addStep(ShellCommand(
821 description = "Generating config.buildinfo, version.buildinfo and feeds.buildinfo",
822 command = "make -j1 buildinfo V=s || true",
827 factory.addStep(ShellCommand(
828 name = "json_overview_image_info",
829 description = "Generate profiles.json in target folder",
830 command = "make -j1 json_overview_image_info V=s || true",
835 factory.addStep(ShellCommand(
837 description = "Calculating checksums",
838 command=["make", "-j1", "checksum", "V=s"],
843 if enable_kmod_archive:
844 factory.addStep(ShellCommand(
846 description = "Creating kmod directory",
847 command=["mkdir", "-p", Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/kmods/%(prop:kernelversion)s", target=ts[0], subtarget=ts[1])],
851 factory.addStep(ShellCommand(
852 name = "kmodprepare",
853 description = "Preparing kmod archive",
854 command=["rsync", "--include=/kmod-*.ipk", "--exclude=*", "-va",
855 Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/packages/", target=ts[0], subtarget=ts[1]),
856 Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/kmods/%(prop:kernelversion)s/", target=ts[0], subtarget=ts[1])],
860 factory.addStep(ShellCommand(
862 description = "Indexing kmod archive",
863 command=["make", Interpolate("-j%(prop:nproc:-1)s"), "package/index", "V=s", "CONFIG_SIGNED_PACKAGES=",
864 Interpolate("PACKAGE_SUBDIRS=bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/kmods/%(prop:kernelversion)s/", target=ts[0], subtarget=ts[1])],
870 if ini.has_option("gpg", "key") or usign_key is not None:
871 factory.addStep(MasterShellCommand(
872 name = "signprepare",
873 description = "Preparing temporary signing directory",
874 command = ["mkdir", "-p", "%s/signing" %(work_dir)],
878 factory.addStep(ShellCommand(
880 description = "Packing files to sign",
881 command = Interpolate("find bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/ bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/kmods/ -mindepth 1 -maxdepth 2 -type f -name sha256sums -print0 -or -name Packages -print0 | xargs -0 tar -czf sign.tar.gz", target=ts[0], subtarget=ts[1]),
885 factory.addStep(FileUpload(
886 workersrc = "sign.tar.gz",
887 masterdest = "%s/signing/%s.%s.tar.gz" %(work_dir, ts[0], ts[1]),
891 factory.addStep(MasterShellCommand(
893 description = "Signing files",
894 command = ["%s/signall.sh" %(scripts_dir), "%s/signing/%s.%s.tar.gz" %(work_dir, ts[0], ts[1])],
895 env = { 'CONFIG_INI': os.getenv("BUILDMASTER_CONFIG", "./config.ini") },
899 factory.addStep(FileDownload(
900 name = "dlsigntargz",
901 mastersrc = "%s/signing/%s.%s.tar.gz" %(work_dir, ts[0], ts[1]),
902 workerdest = "sign.tar.gz",
906 factory.addStep(ShellCommand(
908 description = "Unpacking signed files",
909 command = ["tar", "-xzf", "sign.tar.gz"],
914 factory.addStep(ShellCommand(
916 description = "Preparing upload directory structure",
917 command = ["mkdir", "-p", Interpolate("tmp/upload/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s", target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix)],
921 factory.addStep(ShellCommand(
922 name = "linkprepare",
923 description = "Preparing repository symlink",
924 command = ["ln", "-s", "-f", Interpolate("../packages-%(kw:basever)s", basever=GetBaseVersion()), Interpolate("tmp/upload/%(kw:prefix)spackages", prefix=GetVersionPrefix)],
925 doStepIf = IsNoMasterBuild,
929 if enable_kmod_archive:
930 factory.addStep(ShellCommand(
931 name = "kmoddirprepare",
932 description = "Preparing kmod archive upload directory",
933 command = ["mkdir", "-p", Interpolate("tmp/upload/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s/kmods/%(prop:kernelversion)s", target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix)],
937 factory.addStep(ShellCommand(
939 description = "Uploading directory structure",
940 command = ["rsync", "-az"] + rsync_bin_defopts + ["tmp/upload/", "%s/" %(rsync_bin_url)],
941 env={'RSYNC_PASSWORD': rsync_bin_key},
942 haltOnFailure = True,
947 # download remote sha256sums to 'target-sha256sums'
948 factory.addStep(ShellCommand(
949 name = "target-sha256sums",
950 description = "Fetching remote sha256sums for target",
951 command = ["rsync", "-z"] + rsync_bin_defopts + [Interpolate("%(kw:rsyncbinurl)s/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s/sha256sums", rsyncbinurl=rsync_bin_url, target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix), "target-sha256sums"],
952 env={'RSYNC_PASSWORD': rsync_bin_key},
954 haltOnFailure = False,
955 flunkOnFailure = False,
956 warnOnFailure = False,
959 # build list of files to upload
960 factory.addStep(FileDownload(
961 name = "dlsha2rsyncpl",
962 mastersrc = scripts_dir + '/sha2rsync.pl',
963 workerdest = "../sha2rsync.pl",
967 factory.addStep(ShellCommand(
969 description = "Building list of files to upload",
970 command = ["../sha2rsync.pl", "target-sha256sums", Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/sha256sums", target=ts[0], subtarget=ts[1]), "rsynclist"],
971 haltOnFailure = True,
974 factory.addStep(FileDownload(
976 mastersrc = scripts_dir + '/rsync.sh',
977 workerdest = "../rsync.sh",
981 # upload new files and update existing ones
982 factory.addStep(ShellCommand(
983 name = "targetupload",
984 description = "Uploading target files",
985 command=["../rsync.sh", "--exclude=/kmods/", "--files-from=rsynclist", "--delay-updates", "--partial-dir=.~tmp~%s~%s" %(ts[0], ts[1])] + rsync_bin_defopts +
986 ["-a", Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/", target=ts[0], subtarget=ts[1]),
987 Interpolate("%(kw:rsyncbinurl)s/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s/", rsyncbinurl=rsync_bin_url, target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix)],
988 env={'RSYNC_PASSWORD': rsync_bin_key},
989 haltOnFailure = True,
993 # delete files which don't exist locally
994 factory.addStep(ShellCommand(
995 name = "targetprune",
996 description = "Pruning target files",
997 command=["../rsync.sh", "--exclude=/kmods/", "--delete", "--existing", "--ignore-existing", "--delay-updates", "--partial-dir=.~tmp~%s~%s" %(ts[0], ts[1])] + rsync_bin_defopts +
998 ["-a", Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/", target=ts[0], subtarget=ts[1]),
999 Interpolate("%(kw:rsyncbinurl)s/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s/", rsyncbinurl=rsync_bin_url, target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix)],
1000 env={'RSYNC_PASSWORD': rsync_bin_key},
1001 haltOnFailure = True,
1006 if enable_kmod_archive:
1007 factory.addStep(ShellCommand(
1008 name = "kmodupload",
1009 description = "Uploading kmod archive",
1010 command=["../rsync.sh", "--delete", "--delay-updates", "--partial-dir=.~tmp~%s~%s" %(ts[0], ts[1])] + rsync_bin_defopts +
1011 ["-a", Interpolate("bin/targets/%(kw:target)s/%(kw:subtarget)s%(prop:libc)s/kmods/%(prop:kernelversion)s/", target=ts[0], subtarget=ts[1]),
1012 Interpolate("%(kw:rsyncbinurl)s/%(kw:prefix)stargets/%(kw:target)s/%(kw:subtarget)s/kmods/%(prop:kernelversion)s/", rsyncbinurl=rsync_bin_url, target=ts[0], subtarget=ts[1], prefix=GetVersionPrefix)],
1013 env={'RSYNC_PASSWORD': rsync_bin_key},
1014 haltOnFailure = True,
1019 if rsync_src_url is not None:
1020 factory.addStep(ShellCommand(
1021 name = "sourcelist",
1022 description = "Finding source archives to upload",
1023 command = "find dl/ -maxdepth 1 -type f -not -size 0 -not -name '.*' -not -name '*.hash' -not -name '*.dl' -newer .config -printf '%f\\n' > sourcelist",
1024 haltOnFailure = True
1027 factory.addStep(ShellCommand(
1028 name = "sourceupload",
1029 description = "Uploading source archives",
1030 command=["../rsync.sh", "--files-from=sourcelist", "--size-only", "--delay-updates"] + rsync_src_defopts +
1031 [Interpolate("--partial-dir=.~tmp~%(kw:target)s~%(kw:subtarget)s~%(prop:workername)s", target=ts[0], subtarget=ts[1]), "-a", "dl/", "%s/" %(rsync_src_url)],
1032 env={'RSYNC_PASSWORD': rsync_src_key},
1033 haltOnFailure = True,
1038 factory.addStep(ShellCommand(
1040 description = "Reporting disk usage",
1041 command=["df", "-h", "."],
1042 env={'LC_ALL': 'C'},
1043 haltOnFailure = False,
1044 flunkOnFailure = False,
1045 warnOnFailure = False,
1049 factory.addStep(ShellCommand(
1051 description = "Reporting estimated file space usage",
1052 command=["du", "-sh", "."],
1053 env={'LC_ALL': 'C'},
1054 haltOnFailure = False,
1055 flunkOnFailure = False,
1056 warnOnFailure = False,
1060 factory.addStep(ShellCommand(
1061 name = "ccachestat",
1062 description = "Reporting ccache stats",
1063 command=["ccache", "-s"],
1064 env = MakeEnv(overrides={ 'PATH': ["${PATH}", "./staging_dir/host/bin"] }),
1065 want_stderr = False,
1066 haltOnFailure = False,
1067 flunkOnFailure = False,
1068 warnOnFailure = False,
1072 c['builders'].append(BuilderConfig(name=target, workernames=workerNames, factory=factory, nextBuild=GetNextBuild))
1074 c['schedulers'].append(schedulers.Triggerable(name="trigger_%s" % target, builderNames=[ target ]))
1075 force_factory.addStep(steps.Trigger(
1076 name = "trigger_%s" % target,
1077 description = "Triggering %s build" % target,
1078 schedulerNames = [ "trigger_%s" % target ],
1079 set_properties = { "reason": Property("reason"), "tag": TagPropertyValue },
1080 doStepIf = IsTargetSelected(target)
1084 ####### STATUS TARGETS
1086 # 'status' is a list of Status Targets. The results of each build will be
1087 # pushed to these targets. buildbot/status/*.py has a variety to choose from,
1088 # including web pages, email senders, and IRC bots.
1090 if "status_bind" in inip1:
1092 'port': inip1.get("status_bind"),
1094 'waterfall_view': True,
1095 'console_view': True,
1100 if "status_user" in inip1 and "status_password" in inip1:
1101 c['www']['auth'] = util.UserPasswordAuth([
1102 (inip1.get("status_user"), inip1.get("status_password"))
1104 c['www']['authz'] = util.Authz(
1105 allowRules=[ util.AnyControlEndpointMatcher(role="admins") ],
1106 roleMatchers=[ util.RolesFromUsername(roles=["admins"], usernames=[inip1.get("status_user")]) ]
1110 if ini.has_section("irc"):
1112 irc_host = iniirc.get("host", None)
1113 irc_port = iniirc.getint("port", 6667)
1114 irc_chan = iniirc.get("channel", None)
1115 irc_nick = iniirc.get("nickname", None)
1116 irc_pass = iniirc.get("password", None)
1118 if irc_host and irc_nick and irc_chan:
1119 irc = reporters.IRC(irc_host, irc_nick,
1121 password = irc_pass,
1122 channels = [ irc_chan ],
1123 notify_events = [ 'exception', 'problem', 'recovery' ]
1126 c['services'].append(irc)
1128 c['revlink'] = util.RevlinkMatch([
1129 r'https://git.openwrt.org/openwrt/(.*).git'
1131 r'https://git.openwrt.org/?p=openwrt/\1.git;a=commit;h=%s')
1136 # This specifies what database buildbot uses to store its state. You can leave
1137 # this at its default for all but the largest installations.
1138 'db_url' : "sqlite:///state.sqlite",
1141 c['buildbotNetUsageData'] = None