diff options
author | Magnus Granberg <zorry@gentoo.org> | 2022-07-27 13:01:45 +0200 |
---|---|---|
committer | Magnus Granberg <zorry@gentoo.org> | 2022-07-27 13:01:45 +0200 |
commit | 1592e1d7979e55f71c676869c1d3cc1a21064860 (patch) | |
tree | eb690eb57e780d35434bf9789ef6c1788814f30d | |
parent | Add support for use of rootworkdir (diff) | |
download | tinderbox-cluster-1592e1d7979e55f71c676869c1d3cc1a21064860.tar.gz tinderbox-cluster-1592e1d7979e55f71c676869c1d3cc1a21064860.tar.bz2 tinderbox-cluster-1592e1d7979e55f71c676869c1d3cc1a21064860.zip |
Use worker for Version check (get aux db)
Signed-off-by: Magnus Granberg <zorry@gentoo.org>
-rw-r--r-- | buildbot_gentoo_ci/config/builders.py | 24 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/buildfactorys.py | 52 | ||||
-rw-r--r-- | buildbot_gentoo_ci/config/workers.py | 18 | ||||
-rw-r--r-- | buildbot_gentoo_ci/steps/version.py | 225 | ||||
-rw-r--r-- | docker/GentooBuildbotWorkerDefault.Dockerfile | 16 |
5 files changed, 241 insertions, 94 deletions
diff --git a/buildbot_gentoo_ci/config/builders.py b/buildbot_gentoo_ci/config/builders.py index e168cf5..82552fa 100644 --- a/buildbot_gentoo_ci/config/builders.py +++ b/buildbot_gentoo_ci/config/builders.py @@ -20,6 +20,14 @@ def CanWorkerBuildProject(builder, wfb, request): print('no worker') return False +# Use same worker as update_cpv_data was done by so we have same git commit +def CanWorkerUpdateV(builder, wfb, request): + print(request.properties['cp_worker']) + print(wfb) + if wfb.worker.workername == request.properties['cp_worker']: + return True + return False + def gentoo_builders(worker_data): b = [] g_ci_w = gentoo_ci_workers(worker_data) @@ -39,24 +47,24 @@ def gentoo_builders(worker_data): factory=buildfactorys.update_repo_check() ) ) - # Use multiplay workers depend on Property(cp) - # if cp do not match next one, use diffrent worker then - # or first cp have done its buildsteps. - # first LocalWorker need to be done before we can use mulitplay workers (git pull) + # update cpv in db and call build_request_data + #FIXME: look so we don't run parallel with diffrent worker + # (builders.UpdateRepos step) b.append(util.BuilderConfig( name='update_cpv_data', - workernames=g_ci_w.getWorkersUuid('local'), + workernames=g_ci_w.getWorkersUuid('log')[0], workerbuilddir='builds', collapseRequests=False, - factory=buildfactorys.update_db_cp() + factory=buildfactorys.update_db_cpv() ) ) # Use multiplay workers b.append(util.BuilderConfig( name='update_v_data', - workernames=g_ci_w.getWorkersUuid('local'), + workername=g_ci_w.getWorkersUuid('log')[0], workerbuilddir='builds', collapseRequests=False, + canStartBuild=CanWorkerUpdateV, factory=buildfactorys.update_db_v() ) ) @@ -80,7 +88,7 @@ def gentoo_builders(worker_data): # Use multiplay workers b.append(util.BuilderConfig( name='parse_build_log', - workernames=g_ci_w.getWorkersUuid('log'), + workernames=g_ci_w.getWorkersUuid('log')[1:], collapseRequests=False, factory=buildfactorys.parse_build_log() ) diff --git a/buildbot_gentoo_ci/config/buildfactorys.py b/buildbot_gentoo_ci/config/buildfactorys.py index d07e31b..67447aa 100644 --- a/buildbot_gentoo_ci/config/buildfactorys.py +++ b/buildbot_gentoo_ci/config/buildfactorys.py @@ -22,12 +22,13 @@ def update_db_check(): # return profile_repository, project f.addStep(update_db.GetDataGentooCiProject()) # update the repos - f.addStep(update_db.TriggerUpdateRepositorys()) + #f.addStep(update_db.TriggerUpdateRepositorys()) # Make a for loop and trigger new builders with cpv from git_changes # return cpv, repository, project_data f.addStep(update_db.TriggerCheckForCPV()) return f +# we run repo update in update_db_cpv instead def update_repo_check(): f = util.BuildFactory() # FIXME: 6 @@ -40,9 +41,14 @@ def update_repo_check(): f.addStep(repos.CheckRepository()) return f -def update_db_cp(): +def update_db_cpv(): f = util.BuildFactory() - # FIXME: 2 + # set needed Propertys + f.addStep(package.SetupPropertys()) + # update the repositorys listed in project_repository + f.addStep(builders.UpdateRepos()) + # add repo.conf + #f.addStep(portage.SetReposConf()) # if categorys in db # return category_data # add check category path step at end @@ -57,33 +63,25 @@ def update_db_cp(): # add package to db step # return package_data f.addStep(package.CheckP()) - # Trigger new builders with v from cpv - # return package_data, cpv, repository_data, project_data + # Trigger update_db_v f.addStep(package.TriggerCheckForV()) return f def update_db_v(): f = util.BuildFactory() - # FIXME: 3 - # if version in db - # return version_data - f.addStep(version.GetVData()) - # check path and hash - f.addStep(version.CheckPathHash()) - # if not path - # if not hash - # add deleted stage att end - # add version to db stage - # add version metadata to db - # add version to build check - # else - # add deleted stage att end - # add version to build check stage att end - # else - # add version to db - # add version metadata to db - # add version to build check - f.addStep(version.CheckV()) + # set needed Propertys + f.addStep(version.SetupPropertys()) + # check path + f.addStep(version.CheckPath()) + # if path + # if we have a old version + # mark that version as old + # add new version to db + # add version metadata to db + # Trigger build_request_check + # else + # mark version as old + f.addStep(version.SetupStepsForCheckV()) return f def build_request_check(): @@ -149,8 +147,8 @@ def run_build_request(): # workdir='/etc/portage/')) f.addStep(portage.SetMakeProfile()) # setup repos.conf dir - f.addStep(buildbot_steps.MakeDirectory(dir="repos.conf", - workdir='/etc/portage/')) + #f.addStep(buildbot_steps.MakeDirectory(dir="repos.conf", + # workdir='/etc/portage/')) f.addStep(portage.SetReposConf()) # setup make.conf f.addStep(portage.SetMakeConf()) diff --git a/buildbot_gentoo_ci/config/workers.py b/buildbot_gentoo_ci/config/workers.py index 42c6631..ea05f89 100644 --- a/buildbot_gentoo_ci/config/workers.py +++ b/buildbot_gentoo_ci/config/workers.py @@ -34,7 +34,7 @@ def log_docker_images(props): @util.renderer def docker_volumes(props): volumes_list = [] - #FIXME: set in master.cfg + #FIXME: set in master.cfg /srv/gentoo/portage/ src_dir = '/srv/gentoo/portage/' + props.getProperty('project_uuid') dest_dir = '/var/cache/portage' #add distdir @@ -43,6 +43,18 @@ def docker_volumes(props): volumes_list.append(src_dir + '/packages' + ':' + dest_dir + '/packages') return volumes_list +#NOTE: source permission set to user/group buildbot +@util.renderer +def docker_volumes_repositorys(props): + volumes_list = [] + #FIXME: set in master.cfg /srv/gentoo/portage/repos + src_dir = '/srv/gentoo/portage/repos' + #FIXME: set to getProperty('builddir') + repositorys + dest_dir = '/var/lib/buildbot_worker/repositorys' + #add distdir + volumes_list.append(':'.join([src_dir, dest_dir])) + return volumes_list + def gentoo_workers(worker_data): w = [] g_ci_w = gentoo_ci_workers(worker_data) @@ -79,10 +91,10 @@ def gentoo_workers(worker_data): log_worker['password'], docker_host='tcp://192.168.1.12:2375', image=log_docker_images, - #volumes=docker_volumes, + volumes=docker_volumes_repositorys, hostconfig=docker_hostconfig, followStartupLogs=True, masterFQDN='192.168.1.5', - build_wait_timeout=3600 + #build_wait_timeout=3600 )) return w diff --git a/buildbot_gentoo_ci/steps/version.py b/buildbot_gentoo_ci/steps/version.py index 86d5963..dbeaf14 100644 --- a/buildbot_gentoo_ci/steps/version.py +++ b/buildbot_gentoo_ci/steps/version.py @@ -1,13 +1,10 @@ # Copyright 2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 -import re import os -import git -from portage.xml.metadata import MetaDataXML -from portage.checksum import perform_checksum from portage.versions import cpv_getversion, pkgsplit, catpkgsplit +from portage import _pms_eapi_re, eapi_is_supported, auxdbkeys from twisted.internet import defer from twisted.python import log @@ -17,13 +14,72 @@ from buildbot.process.results import SUCCESS from buildbot.process.results import FAILURE from buildbot.process.results import WARNINGS from buildbot.process.results import SKIPPED +from buildbot.process import remotecommand from buildbot.plugins import steps -from buildbot_gentoo_ci.steps import portage as portage_steps +def GetPythonVersion(pyprop): + py_list = pyprop.replace(' ', '').replace('\n ', '').split('.') + return '.'.join([py_list[0], py_list[1]]).lower() + +def PersOutputOfGetEapi(rc, stdout, stderr): + eapi = None + # split the lines + for line in stdout.split('\n'): + if line.startswith('EAPI'): + print(line[-1]) + m = _pms_eapi_re.match(line) + if m is not None: + eapi = m.group(2) + print(eapi) + if eapi is None or not eapi_is_supported(eapi): + print('ERROR: invalid eapi or not found') + eapi = False + else: + print(eapi) + return { + 'eapi' : eapi + } + +def PersOutputOfGetAuxdb(rc, stdout, stderr): + metadata = None + NoSplit = ['DESCRIPTION'] + ignore_list = ['SRC_URI'] + #make dict of the stout + index = 1 + metadata_line_dict = {} + for text_line in stdout.splitlines(): + metadata_line_dict[index] = text_line + index = index + 1 + if not stderr == '': + print('stderr') + # should have 22 lines + if len(auxdbkeys) != index -1: + # number of lines is incorrect. + print('ERROR: Number of lines is incorrect') + print(metadata_line_dict) + return { + 'auxdb' : metadata + } + # split all keys to list instead of speces + metadata = {} + i = 1 + for key in auxdbkeys: + if metadata_line_dict[i][-1] == '=' or key in ignore_list: + metadata[key] = False + else: + if ' ' in metadata_line_dict[i] and key not in NoSplit: + metadata[key] = metadata_line_dict[i].replace(key + '=', '').split(' ') + else: + metadata[key] = [] + metadata[key].append(metadata_line_dict[i].replace(key + '=', '')) + i = i + 1 + return { + 'auxdb' : metadata + } -class GetVData(BuildStep): - - name = 'GetVData' +class SetEnvForGetAuxdb(BuildStep): + + name = 'SetEnvForGetAuxdb' description = 'Running' descriptionDone = 'Ran' descriptionSuffix = None @@ -35,16 +91,49 @@ class GetVData(BuildStep): @defer.inlineCallbacks def run(self): - # set cwd to builddir - yield os.chdir(self.getProperty("builddir")) - self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] - print(self.getProperty("cpv")) - self.version = yield cpv_getversion(self.getProperty("cpv")) - print(self.version) - self.old_version_data = yield self.gentooci.db.versions.getVersionByName(self.version, self.getProperty("package_data")['uuid']) - print(self.old_version_data) - self.setProperty("old_version_data", self.old_version_data, 'old_version_data') - self.setProperty("version", self.version, 'version') + ebuild_commands = [] + ebuild_env = {} + if not self.getProperty("eapi"): + return FAILURE + #Setup ENV + category = yield catpkgsplit(self.getProperty("cpv"))[0] + package = yield catpkgsplit(self.getProperty("cpv"))[1] + version = yield catpkgsplit(self.getProperty("cpv"))[2] + revision = yield catpkgsplit(self.getProperty("cpv"))[3] + python_version = GetPythonVersion(self.getProperty("python_version")) + portage_bin_path = yield os.path.join('/usr/lib/portage', python_version) + ebuild_sh_bin = 'ebuild.sh' + ebuild_sh_path = yield os.path.join(portage_bin_path, ebuild_sh_bin) + ebuild_env['EAPI'] = self.getProperty("eapi") + #ebuild_env['PORTAGE_DEBUG'] = '1' + ebuild_env['EBUILD_PHASE'] = 'depend' + ebuild_env['CATEGORY'] = category + ebuild_env['P'] = package + '-' + version + ebuild_env['PN'] = package + ebuild_env['PR'] = revision + ebuild_env['PV'] = version + if revision == 'r0': + ebuild_env['PF'] = ebuild_env['P'] + ebuild_env['PVR'] = version + else: + ebuild_env['PF'] = ebuild_env['P'] + '-' + revision + ebuild_env['PVR'] = version + '-' + revision + ebuild_env['PORTAGE_BIN_PATH'] = portage_bin_path + ebuild_env['EBUILD'] = self.getProperty("ebuild_file") + ebuild_env['PORTAGE_PIPE_FD'] = '1' + ebuild_env['WORKDIR'] = yield os.path.join('/var/tmp/portage', 'portage', category, ebuild_env['PF'], 'work') + #FIXME: get a list of repository_path + ebuild_env['PORTAGE_ECLASS_LOCATIONS'] = self.getProperty("repository_path") + yield self.build.addStepsAfterCurrentStep([steps.SetPropertyFromCommand( + name = 'RunGetAuxdb', + haltOnFailure = True, + flunkOnFailure = True, + command=[ebuild_sh_path, 'depend'], + env=ebuild_env, + workdir=self.getProperty("builddir"), + strip=False, + extract_fn=PersOutputOfGetAuxdb + )]) return SUCCESS class AddVersion(BuildStep): @@ -65,7 +154,7 @@ class AddVersion(BuildStep): self.version_data = {} self.version_data['name'] = self.getProperty("version") self.version_data['package_uuid'] = self.getProperty("package_data")['uuid'] - self.version_data['file_hash'] = self.getProperty("ebuild_file_hash") + self.version_data['file_hash'] = 'None' self.version_data['commit_id'] = self.getProperty("commit_id") self.version_data['uuid'] = yield self.gentooci.db.versions.addVersion( self.version_data['name'], @@ -77,8 +166,6 @@ class AddVersion(BuildStep): self.setProperty("version_data", self.version_data, 'version_data') return SUCCESS - - class GetCommitdata(BuildStep): name = 'GetCommitdata' @@ -129,7 +216,7 @@ class AddVersionKeyword(BuildStep): self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] self.version_keyword_dict = {} auxdb = self.getProperty("auxdb")['KEYWORDS'] - if auxdb is None or not isinstance(auxdb, list): + if not auxdb or not isinstance(auxdb, list): self.version_keyword_dict = None self.setProperty('version_keyword_dict', self.version_keyword_dict, 'version_keyword_dict') return SUCCESS @@ -173,7 +260,7 @@ class AddVersionRestrictions(BuildStep): def run(self): self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] auxdb = self.getProperty("auxdb")['RESTRICT'] - if auxdb is None or not isinstance(auxdb, list): + if not auxdb or not isinstance(auxdb, list): return SKIPPED for restrict in auxdb: version_metadata_data = {} @@ -202,7 +289,7 @@ class AddVersionIUse(BuildStep): def run(self): self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] auxdb = self.getProperty("auxdb")['IUSE'] - if auxdb is None or not isinstance(auxdb, list): + if not auxdb or not isinstance(auxdb, list): return SKIPPED for iuse in auxdb: version_metadata_data = {} @@ -215,9 +302,9 @@ class AddVersionIUse(BuildStep): version_metadata_data['value']) return SUCCESS -class CheckPathHash(BuildStep): - - name = 'CheckPathHash' +class CheckPath(BuildStep): + + name = 'CheckPath' description = 'Running' descriptionDone = 'Ran' descriptionSuffix = None @@ -230,23 +317,24 @@ class CheckPathHash(BuildStep): @defer.inlineCallbacks def run(self): self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] - self.repository_basedir_db = yield os.path.join(self.master.basedir, 'repositorys') - self.repository_path = yield os.path.join(self.repository_basedir_db, self.getProperty("repository_data")['name']) + self.repository_basedir = yield os.path.join(self.getProperty("rootworkdir"), self.getProperty('portage_repos_path')[1:]) + self.repository_path = yield os.path.join(self.repository_basedir, self.getProperty("repository_data")['name']) self.cp_path = yield pkgsplit(self.getProperty("cpv"))[0] self.file_name = yield self.getProperty("package_data")['name'] + '-' + self.getProperty("version") + '.ebuild' self.ebuild_file = yield os.path.join(self.repository_path, self.cp_path, self.file_name) - if os.path.isfile(self.ebuild_file): - self.ebuild_file_hash = yield perform_checksum(self.ebuild_file, "SHA256")[0] - else: + print(self.ebuild_file) + cmd = remotecommand.RemoteCommand('stat', {'file': self.ebuild_file}) + yield self.runCommand(cmd) + if cmd.didFail(): self.ebuild_file = None - self.ebuild_file_hash = None + #self.ebuild_file_hash = None self.setProperty('ebuild_file', self.ebuild_file, 'ebuild_file') - self.setProperty('ebuild_file_hash', self.ebuild_file_hash, 'ebuild_file_hash') + #self.setProperty('ebuild_file_hash', self.ebuild_file_hash, 'ebuild_file_hash') self.setProperty('repository_path', self.repository_path, 'repository_path') return SUCCESS class TriggerBuildCheck(BuildStep): - + name = 'TriggerBuildCheck' description = 'Running' descriptionDone = 'Ran' @@ -290,9 +378,35 @@ class DeleteOldVersion(BuildStep): yield self.gentooci.db.versions.delVersion(self.getProperty("old_version_data")['uuid']) return SUCCESS -class CheckV(BuildStep): +class SetupPropertys(BuildStep): + + name = 'Setup propertys for checking V' + description = 'Running' + descriptionDone = 'Ran' + descriptionSuffix = None + haltOnFailure = True + flunkOnFailure = True + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + @defer.inlineCallbacks + def run(self): + self.setProperty('portage_repos_path', '/repositorys', 'portage_repos_path') + self.setProperty('rootworkdir', '/var/lib/buildbot_worker', 'rootworkdir') + self.gentooci = self.master.namedServices['services'].namedServices['gentooci'] + print(self.getProperty("cpv")) + self.version = yield cpv_getversion(self.getProperty("cpv")) + print(self.version) + self.old_version_data = yield self.gentooci.db.versions.getVersionByName(self.version, self.getProperty("package_data")['uuid']) + print(self.old_version_data) + self.setProperty("old_version_data", self.old_version_data, 'old_version_data') + self.setProperty("version", self.version, 'version') + return SUCCESS + +class SetupStepsForCheckV(BuildStep): - name = 'CheckV' + name = 'Setup steps for Checking V' description = 'Running' descriptionDone = 'Ran' descriptionSuffix = None @@ -307,32 +421,43 @@ class CheckV(BuildStep): addStepVData = [] print(self.getProperty("ebuild_file")) print(self.getProperty("old_version_data")) - print(self.getProperty("ebuild_file_hash")) + #print(self.getProperty("ebuild_file_hash")) if self.getProperty("ebuild_file") is None: + addStepVData.append(TriggerBuildCheck()) if self.getProperty("old_version_data") is None: return WARNINGS else: - addStepVData.append(TriggerBuildCheck()) addStepVData.append(DeleteOldVersion()) else: if self.getProperty("old_version_data") is not None: - if self.getProperty("ebuild_file_hash") == self.getProperty("old_version_data")['file_hash']: - return WARNINGS - else: - addStepVData.append(DeleteOldVersion()) - # setup /etc/portage - addStepVData.append(portage_steps.CheckPathLocal()) - addStepVData.append(portage_steps.SetMakeProfileLocal()) - addStepVData.append(portage_steps.SetReposConfLocal()) - addStepVData.append(portage_steps.SetMakeConfLocal()) + addStepVData.append(DeleteOldVersion()) + # get ebuild aux metadata + addStepVData.append(steps.SetPropertyFromCommand( + name = 'RunGetEAPI', + haltOnFailure = True, + flunkOnFailure = True, + command=['head', '-n', '8', self.getProperty("ebuild_file")], + strip=False, + extract_fn=PersOutputOfGetEapi + )) + addStepVData.append(steps.SetPropertyFromCommand( + name = 'GetPythonVersion', + haltOnFailure = True, + flunkOnFailure = True, + command=['python', '-V'], + strip=False, + property="python_version" + )) + addStepVData.append(SetEnvForGetAuxdb()) # get commit data addStepVData.append(GetCommitdata()) - # get ebuild aux metadata - addStepVData.append(portage_steps.GetAuxMetadata()) + # add version to db addStepVData.append(AddVersion()) + # add metadata to db addStepVData.append(AddVersionKeyword()) addStepVData.append(AddVersionRestrictions()) addStepVData.append(AddVersionIUse()) + # Trigger build_request_check addStepVData.append(TriggerBuildCheck()) yield self.build.addStepsAfterCurrentStep(addStepVData) return SUCCESS diff --git a/docker/GentooBuildbotWorkerDefault.Dockerfile b/docker/GentooBuildbotWorkerDefault.Dockerfile index 7e1a63b..d1197c6 100644 --- a/docker/GentooBuildbotWorkerDefault.Dockerfile +++ b/docker/GentooBuildbotWorkerDefault.Dockerfile @@ -8,19 +8,23 @@ FROM gentoo/stage3:latest COPY --from=portage /var/db/repos/gentoo /var/db/repos/gentoo # Setup portage -# emerge needed deps buildbot-worker, psycopg and sqlalchemy +# emerge needed deps buildbot-worker, psycopg, git and sqlalchemy # get the needed buildbot-worker config RUN echo -e "[binhost]\npriority = 9999\nsync-uri = https://gentoo.osuosl.org/experimental/amd64/binpkg/default/linux/17.1/x86-64/\n" | cat >> /etc/portage/binrepos.conf\ - && echo 'EMERGE_DEFAULT_OPTS="--binpkg-respect-use=n --usepkg=y --getbinpkg=y --autounmask-write --autounmask-continue --autounmask-keep-keywords=y --autounmask-use=y"' | cat >> /etc/portage/make.conf\ - && echo 'FEATURES="-ipc-sandbox -pid-sandbox -network-sandbox -usersandbox -mount-sandbox -sandbox"' | cat >> /etc/portage/make.conf\ + && echo 'EMERGE_DEFAULT_OPTS="--binpkg-respect-use=n --usepkg=y --getbinpkg=y --autounmask-write --autounmask-continue --autounmask-keep-keywords=y --autounmask-use=y"' | cat >> /etc/portage/m> + && echo 'FEATURES="-ipc-sandbox -pid-sandbox -network-sandbox -usersandbox -mount-sandbox sandbox"' | cat >> /etc/portage/make.conf\ && echo 'FEATURES="${FEATURES} parallel-install parallel-fetch -merge-sync"' | cat >> /etc/portage/make.conf\ && echo 'FEATURES="${FEATURES} buildpkg"' | cat >> /etc/portage/make.conf\ && echo 'MAKEOPTS="-j8"' | cat >> /etc/portage/make.conf\ + && echo 'dev-vcs/git -webdev -gnome-keyring' | cat >> /etc/portage/package.use/git\ && echo 'dev-util/buildbot-worker' | cat >> /etc/portage/package.accept_keywords/buildbot\ - && emerge -qv buildbot-worker sqlalchemy dev-python/psycopg rust-bin\ - && wget https://raw.githubusercontent.com/buildbot/buildbot/master/worker/docker/buildbot.tac\ - && cp buildbot.tac /var/lib/buildbot_worker/buildbot.tac + && echo 'dev-libs/glib' | cat >> /etc/portage/package.mask/git\ + && emerge -qv buildbot-worker sqlalchemy dev-python/psycopg rust-bin dev-vcs/git + #&& chown buildbot:buildbot /var/lib/buildbot_worker +#FIXME: run worker as buildbot (git fail) +#USER buildbot WORKDIR /var/lib/buildbot_worker +RUN wget https://raw.githubusercontent.com/buildbot/buildbot/master/worker/docker/buildbot.tac ENTRYPOINT ["/usr/bin/buildbot-worker"] CMD ["start", "--nodaemon"] |