Buildbot v příkladech

Potřeboval jsem nastavit proces sestavení a dodávky balíčků programů na web z Git repozitáře. A když jsem nedávno viděl na Habru článek o buildbotu (odkaz na konci), rozhodl jsem se, že ho vyzkouším a použiju.

Protože buildbot je distribuovaný systém, bude logické vytvořit samostatný sestavovací host pro každou architekturu a operační systém. V našem případě to budou LXC kontejnery (v případě linuxu) a qemu (v případě windows):

  • vm-srv-build1 — centos 7, zde bude buildbot master a jeden z pracovních strojů
  • vm-srv-build2 — debian 10, pro sestavování DEB balíčků
  • vm-srv-build3 — windows 10, pro sestavování, jak jistě chápete, pod co

Budeme sestavovat Rac GUI — grafické rozhraní k 1C rac pro správu clustera serverů. Pod linuxem budou použity standardní prostředky pro každou OS, pro sestavení exe souboru pod windows z tcl skriptu se používá freewrap.

Instalace

GNU/Linux

K instalaci, dokumentace na internetu je dostatečná 1,2. A nezpůsobuje žádné zvláštní problémy:
Pro mastera:

pip3 install buildbot
pip3 install twisted
pip3 install autobahn
pip3 install pysqlite3
pip3 install sqlalchemy sqlalchemy-migrate
pip3 install buildbot-www buildbot-grid-view buildbot-console-view buildbot-waterfall-view
pip3 install python-dateutil

Pro 'pracovníky' stačí toto:

pip3 install buildbot-worker

Samozřejmě, správnějším by bylo sestavit balíčky pro každý OS, ale to není téma tohoto článku. Také vynecháme popis nastavení kontejnerů pro práci, zmíním jen, že používám ProxMox VE. A ještě bude potřeba nainstalovat balíčky pro každou OS potřebné pro sestavení (centos: rpmdevtools atd.; debian: build-essential, dh-make, pbuilder atd.)

Sestavení projektů a služby buildbot budou spuštěny pod jménem neprivilégovaného uživatele, proto je nutné ho vytvořit na všech hostitelích zapojených do procesu:


adduser buildbot

Dále nastavíme automatické spouštění služeb, a to na každém z hostitelů (kontejnerů):

Systemd jednotka pro spuštění mastera:

touch /etc/systemd/buildbot-master.service 

[Unit]
Description=BuildBot master service
After=network.target

[Service]
User=buildbot
Group=buildbot
WorkingDirectory=/home/buildbot/master
ExecStart=/usr/local/bin/buildbot start --nodaemon
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

a 'pracovníka'

touch /etc/systemd/buildbot-worker.service 

[Unit]
Description=BuildBot worker service
After=network.target

[Service]master
User=buildbot
Group=buildbot
WorkingDirectory=/home/buildbot/worker
ExecStart=/usr/local/bin/buildbot-worker start --nodaemon

[Install]
WantedBy=buildbot-master.service

Protože všechny skripty (v našem případě) leží v /usr/local/, je nutné zapsat cestu k nim do proměnných prostředí:

nano /root/.bash_profile

PATH=$PATH:$HOME/.local/bin:$HOME/bin:/usr/local/bin

Poté je možné vytvořit katalogovou infrastrukturu pro "pracovníky" (na všech hostitelích). K tomu se zaregistruje uživatel buildbot a vykonají se následující příkazy:

Na prvním hostiteli vm-srv-build1:

su - buildbot
mkdir /home/buildbot/worker
cd ~
buildbot-worker create-worker --umask=0o22 --keepalive=60 worker vm-srv-build1:4000 CentOS 123456

Na druhém hostiteli vm-srv-build2:

su - buildbot
mkdir /home/buildbot/worker
cd ~
buildbot-worker create-worker --umask=0o22 --keepalive=60 worker vm-srv-build1:4000 Debian-10 123456

Na pracovních hostitelích lze spustit službu buildbot-worker

systemctl start buildbot-worker

MS Windows

Jako "pracovníka" pro sestavení pod Windows se použije virtuální stroj s nejnovější verzí Win10.
Pro práci bude potřeba:

Jakmile se nainstalují všechny výše uvedené položky, lze nainstalovat i samotný buildbot:

pip install buildbot-worker

Vytvoříme pracovní adresář

md c:worker

A spustíme

buildbot-worker start c:worker

Pokud vše funguje (viz log c:workertwistd.log), lze našeho "pracovníka" registrovat jako službu přidáním položky do registru s pracovním adresářem (příkazy se vykonávají v PowerShellu spuštěném s administrátorskými právy):

buildbot_worker_windows_service.exe --user VM-SRV-BUILD3buildbot --password 123456 --startup auto install
New-ItemProperty -path Registry::HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesBuildBotParameters -Name directories -PropertyType String -Value c:worker

A lze spustit službu

Start-Service buildbot

S "pracovníky" je tímto konec, dále je nemusíte upravovat, řízení probíhá z masteru.

Nastavení masteru

Pro začátek vytvořme infrastrukturu pro mastera (na hlavním hostiteli), k tomu se zaregistruje uživatel buildbot a vykonají se následující příkazy:

su - buildbot
mkdir /home/buildbot/master
cd ~
buildbot create-master master

Pro hotové balíčky vytvoříme adresář builds

mkdir /home/buildbot/builds

V adresáři /home/buildbot/master/ byl vytvořen soubor master.cfg. Tento soubor představuje kód v Pythonu a obsahuje popis všech mechanismů fungování systému, s ním budeme dále pracovat.

nano /home/buildbot/master/master.cfg

import os, re
from buildbot.plugins import steps, util, schedulers, worker, changes, reporters

c = BuildmasterConfig = {}

# Popis našich pracovníků.
c['workers'] = [ worker.Worker('CentOS', '123456'), worker.Worker('Debian-10', '123456'), worker.Worker('Windows-10', '123456')]
c['protocols'] = {'pb': {'port': 4000}} 

# Určení, který repozitář má master sledovat
c['change_source'] = []
c['change_source'].append(changes.GitPoller(
       repourl = 'https://bitbucket.org/svk28/rac-gui.git',
       project = 'Rac-GUI',
       branches = True,
       pollInterval = 60
      )
)

# Služba spouštění sestavení
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(
       name="Rac-GUI-schedulers",
       change_filter=util.ChangeFilter(branch='master'),
       builderNames=["Rac-GUI-RPM-builder", "Rac-GUI-DEB-builder", "Rac-GUI-WIN-builder"],
       properties = {'owner': 'admin'}
       )
)
@util.renderer

######################################3
# Sestavení RPM balíčku
 rac_gui_build_RPM = util.BuildFactory()

rac_gui_build_RPM.addStep(steps.Git(
       repourl = 'https://bitbucket.org/svk28/rac-gui.git',
       workdir = 'rac-gui',
       haltOnFailure = True,
       submodules = True,
       mode='full',
       progress = True)
)

Abychom automatizovali sestavování balíčků různých verzí, abychom nemuseli činit změny v kódu souboru master.cfg, byly do hlavního skriptu programu rac_gui.tcl na začátku přidány řádky s aktuální verzí a vydáním:

######################################################
#        Rac GUI
...
# version: 1.0.3
# release: 1

A na základě těchto řádků bude buildbot číslovat balíčky. Pro získání dat se používá volání konzolového grep. V buildbotu není možné jednoduše definovat proměnné pro „pracovníky“ (aspoň já jsem nenašel způsob). K tomu slouží vlastnosti (property). Tedy do sestavovacího procesu přidáme kroky pro určení verze a vydání a následně nastavíme vlastnosti version a release. Vlastnosti lze nastavovat různými způsoby, v tomto případě se používá volání konzolového příkazu:

# Добавим определение версии из основного файла
rac_gui_build_RPM.addStep(
       steps.SetPropertyFromCommand(
       command="grep version ../rac-gui/rac_gui.tcl | grep -oE 'b[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}b'", property="version"
       )
)
# Добавим определение релиза из основного файла
rac_gui_build_RPM.addStep(
       steps.SetPropertyFromCommand(
       command="grep release ../rac-gui/rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
       )
)

Získané hodnoty lze dosadit pomocí volání util.Interpolate().

# Запакуем исходники
rac_gui_build_RPM.addStep(
       steps.ShellCommand(
          command=["tar", "czf", util.Interpolate("/home/buildbot/rpmbuild/SOURCES/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"), "../rac-gui"]
       )
)

Je třeba poznamenat, že, protože je host používán i pro ruční sestavování balíčků, bude sestavení probíhat podle standardních cest.

# Копируем spec
rac_gui_build_RPM.addStep(steps.ShellCommand(
       command=["cp", "../rac-gui/rac_gui.spec", "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))

Pro nastavení správných čísel vydání a verze se používá volání standardního sed, tj. příkaz nahrazuje hodnoty uvnitř spec-souboru na potřebné.

# меняем версию
rac_gui_build_RPM.addStep(steps.ShellCommand(
       command=["sed", "-i", util.Interpolate("s/.*Version:.*/Version:t%(prop:version)s/"), "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))
# меняем релиз
rac_gui_build_RPM.addStep(steps.ShellCommand(
       command=["sed", "-i", util.Interpolate("s/.*Release:.*/Release:t%(prop:release)s/"), "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))

# запускаем процесс сборки
rac_gui_build_RPM.addStep(steps.RpmBuild(
       specfile="/home/buildbot/rpmbuild/SPECS/rac_gui.spec",
       dist='.el5',
       topdir='/home/buildbot/rpmbuild',
       builddir='/home/buildbot/rpmbuild/build',
       rpmdir='/home/buildbot/rpmbuild/RPMS',
       sourcedir='/home/buildbot/rpmbuild/SOURCES'
      )
)

Hotový sestavený balíček a archiv se zdrojovými kódy kopírujeme na master. Ale můžete také kopírovat přímo z pracovního souboru do svého repozitáře nebo na web.

# Скопируем файл на мастер
rac_gui_build_RPM.addStep(
       steps.FileUpload(
          workersrc=util.Interpolate("/home/buildbot/rpmbuild/RPMS/noarch/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm"),
          masterdest=util.Interpolate("/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm")
      )
)

rac_gui_build_RPM.addStep(
       steps.FileUpload(
       workersrc=util.Interpolate("/home/buildbot/rpmbuild/SOURCES/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"),
          masterdest=util.Interpolate("/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz")
       )
)

Spustíme na masteru proces kopírování sestavených balíčků na hosting přes FTP. K tomu se používá skript na tcl.

rac_gui_build_RPM.addStep(
        steps.MasterShellCommand(
            command=["/usr/local/bin/deploy-ftp.tcl",
                util.Interpolate("--local-file=/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm"),
                util.Interpolate("--remote-file=uploads/rac-gui/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm")]
        )
)

rac_gui_build_RPM.addStep(
        steps.MasterShellCommand(
            command=["/usr/local/bin/deploy-ftp.tcl",
                util.Interpolate("--local-file=/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"),
                util.Interpolate("--remote-file=uploads/rac-gui/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz")]
        )
)

S RPM jsme skončili. Nyní přejdeme k popisu algoritmu pro sestavení balíčku DEB. Protože procesy pro sestavení balíčků pro různé systémy jsou od sebe nezávislé, mnoho kroků se bude opakovat.


rac_gui_build_DEB = util.BuildFactory()

rac_gui_build_DEB.addStep(steps.Git(
       repourl = 'https://bitbucket.org/svk28/rac-gui.git',
       haltOnFailure = True,
       submodules = True,
       mode='full',
       workdir='build',
       progress = True)
)

# Přidáme určení verze ze základního souboru
rac_gui_build_DEB.addStep(
       steps.SetPropertyFromCommand(
       command="grep version rac_gui.tcl | grep -oE 'b[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}b'", property="version"
       )
)

# Přidáme určení vydání ze základního souboru
rac_gui_build_DEB.addStep(
       steps.SetPropertyFromCommand(
       command="grep release rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
       )
)

# Přejmenujeme spouštitelový soubor
    rac_gui_build_DEB.addStep(steps.ShellCommand(
       command=["mv", "rac_gui.tcl", "racgui"]))

Pro balíček RPM se část níže uvedených procedur realizuje samotným rpm během sestavení a je popsána uvnitř specifikace, pro debiana se to musí udělat zde:

# Поменяем пути к библиотекам
rac_gui_build_DEB.addStep(steps.ShellCommand(
       command=["sed", "-i", "s+^set dir(lib)+set dir(lib) /usr/share/rac-gui/lib ;#+g", "racgui"]))

# Поменяем пути к файлам
rac_gui_build_DEB.addStep(steps.ShellCommand(
       command=["sed", "-i", "s+[pwd]+/usr/share/rac-gui+g", "racgui"]))

# заархивируем исходники
rac_gui_build_DEB.addStep(steps.ShellCommand(
       command=["tar", "czf", util.Interpolate("../rac-gui_%(prop:version)s.orig.tar.gz"), "."]))

# Соберём пакет
rac_gui_build_DEB.addStep(steps.ShellCommand(
       command=["dpkg-buildpackage"]))

# Скопируем файл на мастер
rac_gui_build_DEB.addStep(
       steps.FileUpload(
       workersrc=util.Interpolate("../rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb"),
           masterdest=util.Interpolate("/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb")
       )
)
rac_gui_build_DEB.addStep(
        steps.MasterShellCommand(
            command=["/usr/local/bin/deploy-ftp.tcl",
             util.Interpolate("--local-file=/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb"),
             util.Interpolate("--remote-file=uploads/rac-gui/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb")]
        )
)

A s DEB jsme skončili, nyní Windows!

rac_gui_build_WIN = util.BuildFactory()

rac_gui_build_WIN.addStep(steps.Git(
       repourl = 'https://bitbucket.org/svk28/rac-gui.git',
       haltOnFailure = True,
       submodules = True,
       mode='full',
       workdir='build',
       progress = True)
)

Protože ve Windows normálně není grep a sed (nebo jsou?), použijeme powershell

# Добавим определение версии из основного файла
rac_gui_build_WIN.addStep(
       steps.SetPropertyFromCommand(
       command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'version:') -split 's')[2]",
       property="version"
       )
    )

# Добавим определение релиза из основного файла
rac_gui_build_WIN.addStep(
       steps.SetPropertyFromCommand(
       command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'release:') -split 's')[2]",
       property="release"
       )
)

# Создадим запускаемый файл
rac_gui_build_WIN.addStep(steps.ShellCommand(
       command=["c:binfreewrap.exe", "rac_gui.tcl"]))

# запакуем то, что получилось
rac_gui_build_WIN.addStep(steps.ShellCommand(
       command=["c:Program Files7-zip7z.exe", "a", "-r", util.Interpolate("..rac-gui_%(prop:version)s-%(prop:release)s.win.zip"), "..build"]))

# скопируем на мастер
rac_gui_build_WIN.addStep(
       steps.FileUpload(
       workersrc=util.Interpolate("..rac-gui_%(prop:version)s-%(prop:release)s.win.zip"),
           masterdest=util.Interpolate("/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s.win.zip")
       )
)
# Скопируем файл на хостинг
rac_gui_build_WIN.addStep(
        steps.MasterShellCommand(
            command=["/usr/local/bin/deploy-ftp.tcl",
            util.Interpolate("--local-file=/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s.win.zip"),
            util.Interpolate("--remote-file=uploads/rac-gui/rac-gui_%(prop:version)s-%(prop:release)s.win.zip")]
       )
)

# Тут определяем какие сборщики у нас есть
c['builders'] = [
        util.BuilderConfig(name="Rac-GUI-RPM-builder", workername='CentOS', factory=rac_gui_build_RPM),
        util.BuilderConfig(name="Rac-GUI-DEB-builder", workername='Debian-10', factory=rac_gui_build_DEB),
        util.BuilderConfig(name="Rac-GUI-WIN-builder", workername='Windows-10', factory=rac_gui_build_WIN),
]

Pro oznámení o stavu procesu sestavení použijeme elektrickou poštu


c['services'] = []

template=u'''
    <h4>Stav sestavení: {{ summary }}</h4>
    <p> Použitý pracovník: {{ workername }}</p>
    {% for step in build['steps'] %}
    <p> {{ step['name'] }}: {{ step['result'] }}</p>
    {% endfor %}
    <p><b> -- The Buildbot</b></p>
    '''

mailNotifier = reporters.MailNotifier(fromaddr="builder@domain.cz",
             sendToInterestedUsers=False,
             mode=('all'),
             extraRecipients=["admin@domain.cz"],
             relayhost="mail.domain.cz",
             smtpPort=587,
             smtpUser="builder@domain.cz",
             smtpPassword="******",
             messageFormatter=reporters.MessageFormatter(
                                 template=template, template_type='html',
                                 wantProperties=True, wantSteps=True))

c['services'].append(mailNotifier)

# Hlavní nastavení masteru
c['title'] = "Proces sestavení"
c['titleURL'] = "http://vm-srv-build1:80/"
c['buildbotURL'] = "http://vm-srv-build1/"
c['www'] = dict(port=80,
                plugins=dict(waterfall_view={}, console_view={}, grid_view={}))
c['db'] = {
       'db_url' : "sqlite:////state.sqlite"
}

Uložíme soubor a můžeme zkusit spustit službu master:

systemctl restart buildbot-master

V logu zkontrolujeme, že je s konfigurací všechno v pořádku a všechno funguje tak, jak má. Všichni naši pracovníci by se nyní měli připojit, což bude radostně oznámeno v logu »»’/home/buildbot/master/twistd.log»»’:

 2019-07-24 16:50:35+0300 [-] Načítání buildbot.tac...
 2019-07-24 16:50:35+0300 [-] Načteno.
 2019-07-24 16:50:35+0300 [-] twistd 19.2.1 (\/usr\/bin\/python3.6 3.6.8) se spouští.
 2019-07-24 16:50:35+0300 [-] třída reaktoru: twisted.internet.epollreactor.EPollReactor.
 2019-07-24 16:50:35+0300 [-] Spouští se BuildMaster -- buildbot.verze: 2.3.1
 2019-07-24 16:50:35+0300 [-] Načítání konfigurace z '\/home\/buildbot\/master\/master.cfg'
 2019-07-24 16:50:36+0300 [-] \/usr\/local\/lib\/python3.6\/site-packages\/buildbot\/config.py:90: buildbot.config.ConfigWarning: [0.9.0 a novější] `buildbotNetUsageData` není nakonfigurován a výchozí je základní.
        Tento parametr pomáhá vývojovému týmu buildbota porozumět instalační základně.
        Nebudou shromažďovány žádné osobní informace.
        Odesílají se pouze informace o verzi instalačního softwaru a použití pluginů.
        Můžete se odhlásit nastavením této proměnné na None.
        Nebo se můžete přihlásit pro více informací nastavením na "full".

 2019-07-24 16:50:36+0300 [-] Nastavení databáze s URL 'sqlite:\/state.sqlite'
 2019-07-24 16:50:36+0300 [-] nastavení režimu žurnálu databáze na 'wal'
 2019-07-24 16:50:36+0300 [-] přidávání 1 nové služby, odstraňování 0
 2019-07-24 16:50:36+0300 [-] přidávání 1 nového zdroje změn, odstraňování 0
 2019-07-24 16:50:36+0300 [-] gitpoller: používání pracovního adresáře '\/home\/buildbot\/master\/gitpoller-work'
 2019-07-24 16:50:36+0300 [-] přidávání 3 nových builderů, odstraňování 0
 2019-07-24 16:50:36+0300 [-] přidávání 1 nového plánovače, odstraňování 0
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'waterfall_view'
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'console_view'
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'grid_view'
 2019-07-24 16:50:36+0300 [-] POZNÁMKA: www plugin 'sitenav' je nainstalován, ale není nakonfigurován
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'waterfall_view'
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'console_view'
 2019-07-24 16:50:36+0300 [-] inicializace www pluginu 'grid_view'
 2019-07-24 16:50:36+0300 [-] POZNÁMKA: www plugin 'sitenav' je nainstalován, ale není nakonfigurován
 2019-07-24 16:50:36+0300 [-] BuildbotSite se spouští na 80
 2019-07-24 16:50:36+0300 [-] Spouští se továrna <buildbot.www.service.BuildbotSite objekt na 0x7fe31c2657b8>
 2019-07-24 16:50:36+0300 [-] přidávání 3 nových pracovníků, odstraňování 0
 2019-07-24 16:50:36+0300 [-] PBServerFactory se spouští na 4000
 2019-07-24 16:50:36+0300 [-] Spouští se továrna <twisted.spread.pb.PBServerFactory objekt na 0x7fe31c147470>
 2019-07-24 16:50:37+0300 [-] BuildMaster běží
 2019-07-24 16:50:37+0300 [-] buildbotNetUsageData: odesílání {'installid': 'b6193b126b96689351d2fe95787c5a03fc0879f9', 'versions': {'Python': '3.6.8', 'Buildbot': '2.3.1', 'Twisted': '19.2.1'}, 'platform': {'platform': 'Linux-4.15.18-10- pve-x86_64-with-centos-7.6.1810-Core', 'system': 'Linux', 'machine': 'x86_64', 'processor': 'x86_64', 'python_implementation': 'CPython', 'version': '#1 SMP PVE 4.15.18-32', 'distro': 'centos:7'}, 'plugins': {'buildbot\/worker\/base\/Worker': 3, 'buildbot\/config\/BuilderConfig': 3, 'buildbot\/schedulers\/basic\/SingleBranchScheduler': 1, 'buildbot\/reporters\/mail\/MailNotifier': 1, 'buildbot\/changes\/gitpoller\/GitPoller': 1, 'buildbot\/steps\/worker\/MakeDirectory': 1, 'buildbot\/steps\/source\/git\/Git': 3, 'buildbot\/steps\/shell\/ShellCommand': 9, 'buildbot\/steps\/package\/rpm\/rpmbuild\/RpmBuild': 1}, 'db': 'sqlite', 'mq': 'simple', 'www_plugins': ['waterfall_view', 'console_view', 'grid_view']}
 2019-07-24 16:50:37+0300 [Broker,0,127.0.0.1] pracovník 'CentOS' se připojuje z IPv4Address(type='TCP', host='127.0.0.1', port=37332)
 2019-07-24 16:50:37+0300 [Broker,0,127.0.0.1] Získány informace o pracovníkovi ze 'CentOS'
 2019-07-24 16:50:37+0300 [-] bot připojen
 2019-07-24 16:50:37+0300 [Broker,0,127.0.0.1] Pracovník CentOS připojen k Rac-GUI-RPM-builder
 2019-07-24 16:50:37+0300 [-] buildbotNetUsageData: buildbot.net řekl: ok
 2019-07-24 16:50:39+0300 [Broker,1,192.168.55.15] pracovník 'Windows-10' se připojuje z IPv4Address(type='TCP', host='192.168.5.145', port=49831)
 2019-07-24 16:50:39+0300 [Broker,1,192.168.55.15] Získány informace o pracovníkovi ze 'Windows-10'
 2019-07-24 16:50:40+0300 [-] bot připojen
 2019-07-24 16:50:40+0300 [Broker,1,192.168.55.15] Pracovník Windows-10 připojen k Rac-GUI-WIN-builder
 2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] pracovník 'Debian-10' se připojuje z IPv4Address(type='TCP', host='192.168.5.9', port=44430)
 2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] Získány informace o pracovníkovi ze 'Debian-10'
 2019-07-24 16:50:41+0300 [-] bot připojen
 2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] Pracovník Debian-10 připojen k Rac-GUI-DEB-builder

Tímto je proces nastavení dokončen. Aktuální stav lze zobrazit prostřednictvím webového rozhraní, kde lze také vidět chyby kompilace, restartovat zamrzlý proces, pokud něco nefunguje, atd.

Hned po spuštění našich pracovních strojů lze stav prohlédnout v menu „Builds“ -> „Workers“

Buildbot v příkladech

Jakmile proběhne první kompilace (tj. změny v Git repozitáři), na první stránce se objeví stav procesů.

Buildbot v příkladech

Pokud kliknete myší na požadovaný řádek, otevře se stránka s aktuálním stavem daného procesu, kde je vidět, co se děje, jaké chyby atd.

Buildbot v příkladech

Celou konfiguraci mistra můžete získat zde

import os, re
from buildbot.plugins import steps, util, schedulers, worker, changes, reporters

c= BuildmasterConfig ={}

c['workers'] = [ worker.Worker('CentOS', '123456'), worker.Worker('Debian-10', '123456'), worker.Worker('Windows-10', '123456')]
c['protocols'] = {'pb': {'port': 4000}} 

c['change_source'] = []
c['change_source'].append(changes.GitPoller(
    repourl = 'https://bitbucket.org/svk28/rac-gui.git',
    project = 'Rac-GUI',
    branches = True,
    pollInterval = 600
    ))

# Služba spouštění sestavení
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(
    name="Rac-GUI-schedulers",
    change_filter=util.ChangeFilter(branch='master'),
    builderNames=["Rac-GUI-RPM-builder", "Rac-GUI-DEB-builder", "Rac-GUI-WIN-builder"],
    properties = {'owner': 'admin'}
    ))
@util.renderer
def get_name_version_release(props):
    prog_name = "rac-gui"
    prog_version = "1.0.3"
    prog_release = "3"

    return {
    "prog_name": prog_name
    #"prog_version": prog_version,
    #"prog_release": prog_release
}

rac_gui_build_RPM = util.BuildFactory()

rac_gui_build_RPM.addStep(steps.Git(
    repourl = 'https://bitbucket.org/svk28/rac-gui.git',
    workdir = 'rac-gui',
    haltOnFailure = True,
    submodules = True,
    mode='full',
    progress = True)
    )

# Přidáme určení verze z hlavního souboru
rac_gui_build_RPM.addStep(
    steps.SetPropertyFromCommand(
    command="grep version ../rac-gui/rac_gui.tcl | grep -oE 'b[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}b'", property="version"
    )
)

# Přidáme určení vydání z hlavního souboru
rac_gui_build_RPM.addStep(
    steps.SetPropertyFromCommand(
    command="grep release ../rac-gui/rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
    )
)

rac_gui_build_RPM.addStep(steps.ShellCommand(
    command=["tar", "czf", util.Interpolate("/home/buildbot/rpmbuild/SOURCES/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"), "../rac-gui"]))

rac_gui_build_RPM.addStep(steps.ShellCommand(
    command=["cp", "../rac-gui/rac_gui.spec", "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))

rac_gui_build_RPM.addStep(steps.ShellCommand(
    command=["sed", "-i", util.Interpolate("s/.*Version:.*/Version:t%(prop:version)s/"), "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))

rac_gui_build_RPM.addStep(steps.ShellCommand(
    command=["sed", "-i", util.Interpolate("s/.*Release:.*/Release:t%(prop:release)s/"), "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))

rac_gui_build_RPM.addStep(steps.RpmBuild(
    specfile="/home/buildbot/rpmbuild/SPECS/rac_gui.spec",
    dist='.el5',
    topdir='/home/buildbot/rpmbuild',
    builddir='/home/buildbot/rpmbuild/build',
    rpmdir='/home/buildbot/rpmbuild/RPMS',
    sourcedir='/home/buildbot/rpmbuild/SOURCES'
    ))

# Zkopírujeme soubor na master
rac_gui_build_RPM.addStep(
    steps.FileUpload(
    workersrc=util.Interpolate("/home/buildbot/rpmbuild/RPMS/noarch/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm"),
        masterdest=util.Interpolate("/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm")
    )
)

rac_gui_build_RPM.addStep(
    steps.FileUpload(
    workersrc=util.Interpolate("/home/buildbot/rpmbuild/SOURCES/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"),
        masterdest=util.Interpolate("/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz")
    )
)

rac_gui_build_RPM.addStep(
    steps.MasterShellCommand(
    command=["/usr/local/bin/deploy-ftp.tcl",
     util.Interpolate("--local-file=/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm"),
     util.Interpolate("--remote-file=uploads/rac-gui/rac-gui-%(prop:version)s-%(prop:release)s.noarch.rpm")]
    )
)

rac_gui_build_RPM.addStep(
    steps.MasterShellCommand(
    command=["/usr/local/bin/deploy-ftp.tcl",
     util.Interpolate("--local-file=/home/buildbot/builds/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz"),
     util.Interpolate("--remote-file=uploads/rac-gui/rac-gui-%(prop:version)s-%(prop:release)s.tar.gz")]
    )
)
####################################
##      DEB 
####################################

rac_gui_build_DEB = util.BuildFactory()

rac_gui_build_DEB.addStep(steps.Git(
    repourl = 'https://bitbucket.org/svk28/rac-gui.git',
    haltOnFailure = True,
    submodules = True,
    mode='full',
    workdir='build',
    progress = True)
    )

# Přidáme určení verze z hlavního souboru
rac_gui_build_DEB.addStep(
    steps.SetPropertyFromCommand(
    command="grep version rac_gui.tcl | grep -oE 'b[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}b'", property="version"
    )
)

# Přidáme určení vydání z hlavního souboru
rac_gui_build_DEB.addStep(
    steps.SetPropertyFromCommand(
    command="grep release rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
    )
)

# Přejmenujeme spuštěný soubor
rac_gui_build_DEB.addStep(steps.ShellCommand(
    command=["mv", "rac_gui.tcl", "racgui"]))

# Změníme cesty k knihovnám
rac_gui_build_DEB.addStep(steps.ShellCommand(
    command=["sed", "-i", "s+^set dir(lib)+set dir(lib) /usr/share/rac-gui/lib ;#+g", "racgui"]))

# Změníme cesty k souborům
rac_gui_build_DEB.addStep(steps.ShellCommand(
    command=["sed", "-i", "s+[pwd]+/usr/share/rac-gui+g", "racgui"]))

# Zarchivujeme zdrojové kódy
rac_gui_build_DEB.addStep(steps.ShellCommand(
    command=["tar", "czf", util.Interpolate("../rac-gui_%(prop:version)s.orig.tar.gz"), "."]))

# Sestavíme balíček
rac_gui_build_DEB.addStep(steps.ShellCommand(
    command=["dpkg-buildpackage"]))

# Zkopírujeme soubor na master
rac_gui_build_DEB.addStep(
    steps.FileUpload(
    workersrc=util.Interpolate("../rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb"),
        masterdest=util.Interpolate("/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb")
    )
)

rac_gui_build_DEB.addStep(
    steps.MasterShellCommand(
    command=["/usr/local/bin/deploy-ftp.tcl",
     util.Interpolate("--local-file=/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb"),
     util.Interpolate("--remote-file=uploads/rac-gui/rac-gui_%(prop:version)s-%(prop:release)s_amd64.deb")]
    )
)
############################################
#       WIN
############################################

rac_gui_build_WIN = util.BuildFactory()

rac_gui_build_WIN.addStep(steps.Git(
    repourl = 'https://bitbucket.org/svk28/rac-gui.git',
    haltOnFailure = True,
    submodules = True,
    mode='full',
    workdir='build',
    progress = True)
    )

# Přidáme určení verze z hlavního souboru
rac_gui_build_WIN.addStep(
    steps.SetPropertyFromCommand(
    command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'version:') -split 's')[2]",
    property="version"
    )
)

# Přidáme určení vydání z hlavního souboru
rac_gui_build_WIN.addStep(
    steps.SetPropertyFromCommand(
    command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'release:') -split 's')[2]",
    property="release"
    )
)

# Vytvoříme spustitelný soubor
rac_gui_build_WIN.addStep(steps.ShellCommand(
    command=["c:binfreewrap.exe", "rac_gui.tcl"]))

# Zabalíme to, co jsme dostali
rac_gui_build_WIN.addStep(steps.ShellCommand(
    command=["c:Program Files7-zip7z.exe", "a", "-r", util.Interpolate("..rac-gui_%(prop:version)s-%(prop:release)s.win.zip"), "..build"]))

# Zkopírujeme na master
rac_gui_build_WIN.addStep(
    steps.FileUpload(
    workersrc=util.Interpolate("..rac-gui_%(prop:version)s-%(prop:release)s.win.zip"),
        masterdest=util.Interpolate("/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s.win.zip")
    )
)

# Zkopírujeme soubor na hosting
rac_gui_build_WIN.addStep(
    steps.MasterShellCommand(
    command=["/usr/local/bin/deploy-ftp.tcl",
     util.Interpolate("--local-file=/home/buildbot/builds/rac-gui_%(prop:version)s-%(prop:release)s.win.zip"),
     util.Interpolate("--remote-file=uploads/rac-gui/rac-gui_%(prop:version)s-%(prop:release)s.win.zip")]
    )
)

c['builders'] = [
        util.BuilderConfig(name="Rac-GUI-RPM-builder", workername='CentOS', factory=rac_gui_build_RPM),
        util.BuilderConfig(name="Rac-GUI-DEB-builder", workername='Debian-10', factory=rac_gui_build_DEB),
        util.BuilderConfig(name="Rac-GUI-WIN-builder", workername='Windows-10', factory=rac_gui_build_WIN),
]

c['services'] = []

template=u'''
<h4>Stav sestavení: {{ summary }}</h4>
<p> Použitý pracovník: {{ workername }}</p>
{% for step in build['steps'] %}
<p> {{ step['name'] }}: {{ step['result'] }}</p>
{% endfor %}
<p><b> -- The Buildbot</b></p>
'''

mailNotifier = reporters.MailNotifier(fromaddr="112@icvibor.ru",
          sendToInterestedUsers=False,
          mode=('all'),
          extraRecipients=["my@domain.local"],
          relayhost="mail.domain.local",
          smtpPort=587,
          smtpUser="buildbot@domain.local",
          smtpPassword="**********",
          messageFormatter=reporters.MessageFormatter(
                                 template=template, template_type='html',
                                 wantProperties=True, wantSteps=True))

c['services'].append(mailNotifier)

c['title'] = "Proces budování"
c['titleURL'] = "http://vm-srv-build1:80/"

c['buildbotURL'] = "http://vm-srv-build1/"

c['www'] = dict(port=80,
                plugins=dict(waterfall_view={}, console_view={}, grid_view={}))
c['db'] = {
    'db_url' : "sqlite:///state.sqlite"
}

Materiály

Při přípravě článku byly použity následující materiály:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster