Musiałem skonfigurować proces budowy i dostarczania pakietów oprogramowania z repozytorium Git na stronę. Zobaczywszy niedawno artykuł na Habrze na temat buildbota (link na końcu), postanowiłem spróbować go zastosować.
Ponieważ buildbot to rozproszony system, logiczne będzie stworzenie oddzielnego hosta budowlanego dla każdej architektury i systemu operacyjnego. W naszym przypadku będą to kontenery LXC (w przypadku Linux) oraz qemu (w przypadku Windows):
- vm-srv-build1 — centos 7, tutaj będzie master buildbota oraz jeden z workerów
- vm-srv-build2 — debian 10, do budowy pakietów DEB
- vm-srv-build3 — windows 10, do budowy, sami wiecie, na co
Będziemy budować — graficzny interfejs do 1С rac do zarządzania klastrem serwerów. Dla linuxa będą używane standardowe narzędzia dla każdego systemu operacyjnego, do budowy pliku exe pod windows z tcl-skryptu używa się .
Instalacja
GNU/Linux
Na temat instalacji, w sieci jest wystarczająco dużo dokumentacji ,. A sama instalacja nie sprawia większych problemów:
Dla 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-dateutilDla „workerów” wystarczy to:
pip3 install buildbot-workerOczywiście, bardziej poprawne byłoby budowanie pakietów dla każdego systemu operacyjnego, ale to nie mieści się w ramach artykułu. Omińmy także opis konfiguracji kontenerów do pracy, zaznaczę tylko, że używam ProxMox VE. Ponadto konieczne będzie zainstalowanie pakietów potrzebnych do budowy pod każdy system operacyjny (centos: rpmdevtools itd.; debian: build-essential, dh-make, pbuilder itd.)
Budowa projektów i usługi buildbota będą uruchamiane w imieniu nieuprzywilejowanego użytkownika, dlatego należy go utworzyć na wszystkich hostach biorących udział w procesie:
adduser buildbotNastępnie skonfigurujemy automatyczne uruchamianie usług odpowiednio na każdym z hostów (kontenerów):
Jednostka systemd do uruchomienia mastera:
touch /etc/systemd/buildbot-master.service
[Unit]
Description=Serwis master buildbota
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.targeti „workera”
touch /etc/systemd/buildbot-worker.service
[Unit]
Description=Serwis workera buildbota
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.servicePonieważ wszystkie skrypty (w naszym przypadku) znajdują się w /usr/local/, należy zapisać ścieżkę do nich w zmiennych środowiskowych:
nano /root/.bash_profile
PATH=$PATH:$HOME/.local/bin:$HOME/bin:/usr/local/binPo tym można utworzyć infrastrukturę katalogów dla „pracowników” (na wszystkich hostach), w tym celu rejestrujemy się jako użytkownik buildbot i wykonujemy następujące polecenia:
Na pierwszym hoście vm-srv-build1:
su - buildbot
mkdir /home/buildbot/worker
cd ~
buildbot-worker create-worker --umask=0o22 --keepalive=60 worker vm-srv-build1:4000 CentOS 123456Na drugim hoście 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 123456Na hostach-roboczych można uruchomić usługę buildbot-worker
systemctl start buildbot-workerMS Windows
Jako „pracownika” do kompilacji dla systemu Windows użyjemy maszyny wirtualnej z najnowszą wersją Win10.
Do pracy potrzebne będą:
- Aby zainstalować różne przydatne pakiety w Pythonie, trzeba pobrać
- 7Zip
Po zainstalowaniu wszystkiego, co wyżej wymienione, możemy zainstalować samego buildbota:
pip install buildbot-workerUtworzymy katalog roboczy
md c:workerI uruchomimy
buildbot-worker start c:workerJeśli wszystko działa (zobacz log c:workertwistd.log), można zarejestrować naszego „pracownika” jako usługę, dodając punkt do rejestru z katalogiem roboczym (polecenia wykonuje się w PowerShell uruchomionym jako administrator):
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
I można uruchomić usługę
Start-Service buildbotZ „pracownikami” to wszystko, dalej można ich nie ruszać, cała administracja odbywa się z mastera.
Konfiguracja mastera
Na początek utwórzmy infrastrukturę dla mastera (na głównym hoście), w tym celu rejestrujemy się jako użytkownik buildbot i wykonujemy następujące polecenia:
su - buildbot
mkdir /home/buildbot/master
cd ~
buildbot create-master masterDla gotowych pakietów utworzymy katalog builds
mkdir /home/buildbot/buildsW katalogu /home/buildbot/master/ został utworzony plik master.cfg. Ten plik to kod w Pythonie i zawiera opis wszystkich mechanizmów działania systemu, z którym będziemy pracować w dalszej części.
nano /home/buildbot/master/master.cfgimport os, re
from buildbot.plugins import steps, util, schedulers, worker, changes, reporters
c= BuildmasterConfig ={}
# Opis naszych pracowników.
c['workers'] = [ worker.Worker('CentOS', '123456'), worker.Worker('Debian-10', '123456'), worker.Worker('Windows-10', '123456')]
c['protocols'] = {'pb': {'port': 4000}}
# Wskazanie mistrzowi, który repozytorium śledzić
c['change_source'] = []
c['change_source'].append(changes.GitPoller(
repourl = 'https://bitbucket.org/svk28/rac-gui.git',
project = 'Rac-GUI',
branches = True,
pollInterval = 60
)
)
# Usługa uruchamiająca budowę
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
# Budowa pakietu RPM
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)
)Aby zautomatyzować budowę pakietów różnych wersji, tak aby nie trzeba było grzebać w kodzie pliku master.cfg, w głównym skrypcie programu rac_gui.tcl dodano linijki z aktualną wersją i wydaniem:
######################################################
# Rac GUI
...
# version: 1.0.3
# release: 1I na podstawie tych linijek buildbot będzie numerował pakiety. Do wyciągania danych używa się wywołania konsolowego grep. W buildbot nie można po prostu określić zmiennych dla „pracowników” (przynajmniej nie znalazłem na to sposobu). W tym celu służą właściwości (property). Tzn. do procesu budowy dodajemy kroki do określenia wersji i wydania, a odpowiednio ustawiamy właściwości version i release. Właściwości można ustawiać na różne sposoby, w tym przypadku jest to wywołanie konsolowej komendy:
# Добавим определение версии из основного файла
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"
)
)Wstawianie uzyskanych wartości można zrealizować poprzez wywołanie 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"]
)
)Warto zauważyć, że ponieważ host jest używany również do ręcznej budowy pakietów, budowa przebiegnie według standardowych ścieżek.
# Копируем spec
rac_gui_build_RPM.addStep(steps.ShellCommand(
command=["cp", "../rac-gui/rac_gui.spec", "/home/buildbot/rpmbuild/SPECS/rac_gui.spec"]))Do ustawienia poprawnych numerów wydania i wersji wykorzystuje się standardowe wywołanie sed, tzn. komenda zastępuje wartości w pliku spec na wymagane.
# меняем версию
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'
)
)Gotowy zbudowany pakiet oraz archiwum ze źródłami kopiujemy na master. Można też od razu skopiować z plików roboczych do swojego repozytorium lub na stronę.
# Скопируем файл на мастер
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")
)
)Uruchomimy na masterze proces kopiowania zbudowanych pakietów na hosting przez FTP. W tym celu wykorzystuje się 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")]
)
)Zakończyliśmy już z RPM. Teraz przystąpimy do opisu algorytmu budowy pakietu DEB. Ponieważ procesy budowy pakietów dla różnych systemów są od siebie niezależne, wiele kroków będzie się powtarzać.
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)
)
# Dodamy określenie wersji z głównego pliku
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"
)
)
# Dodamy określenie wydania z głównego pliku
rac_gui_build_DEB.addStep(
steps.SetPropertyFromCommand(
command="grep release rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
)
)
# Zmienimy nazwę uruchamianego pliku
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["mv", "rac_gui.tcl", "racgui"]))Dla pakietu RPM część z poniższych procedur wykonuje samo rpm podczas budowy i są one opisane wewnątrz speca, dla Debiana musimy to zrobić tutaj:
# Поменяем пути к библиотекам
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")]
)
)Z DEB skończone, teraz przechodzimy do 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)
)Ponieważ w systemie Windows standardowo nie ma grep ani sed (czy jest?), będziemy korzystać z PowerShella
# Добавим определение версии из основного файла
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),
]Do powiadamiania o stanie procesu budowy użyjemy poczty elektronicznej
c['services'] = []
template=u'''
<h4>Status budowy: {{ summary }}</h4>
<p> Użyty pracownik: {{ workername }}</p>
{% for step in build['steps'] %}
<p> {{ step['name'] }}: {{ step['result'] }}</p>
{% endfor %}
<p><b> -- Buildbot</b></p>
'''
mailNotifier = reporters.MailNotifier(fromaddr="builder@domain.ru",
sendToInterestedUsers=False,
mode=('all'),
extraRecipients=["admin@domain.ru"],
relayhost="mail.domain.ru",
smtpPort=587,
smtpUser="builder@domain.ru",
smtpPassword="******",
messageFormatter=reporters.MessageFormatter(
template=template, template_type='html',
wantProperties=True, wantSteps=True))
c['services'].append(mailNotifier)
# Podstawowe ustawienia mastera
c['title'] = "Proces budowy"
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"
}Zapisujemy plik i możemy spróbować uruchomić usługę mastera:
systemctl restart buildbot-masterW logu sprawdzimy, czy z konfiguracją wszystko w porządku i wszystko działa w normalnym trybie. Wszyscy nasi pracownicy powinni teraz się połączyć, co będzie radośnie zakomunikowane w logu »»’/home/buildbot/master/twistd.log»»’:
2019-07-24 16:50:35+0300 [-] Ładowanie buildbot.tac...
2019-07-24 16:50:35+0300 [-] Załadowano.
2019-07-24 16:50:35+0300 [-] twistd 19.2.1 (\/usr\/bin\/python3.6 3.6.8) uruchamia się.
2019-07-24 16:50:35+0300 [-] klasa reaktora: twisted.internet.epollreactor.EPollReactor.
2019-07-24 16:50:35+0300 [-] Rozpoczynanie BuildMaster -- buildbot.version: 2.3.1
2019-07-24 16:50:35+0300 [-] Ładowanie konfiguracji 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 i później] `buildbotNetUsageData` nie jest skonfigurowane i domyślnie jest ustawione na podstawowe.
Ten parametr pomaga zespołowi programistów buildbota zrozumieć podstawę instalacji.
Nie zbierane są żadne informacje osobiste.
Wysyłane są tylko informacje o wersji oprogramowania instalacyjnego oraz użyciu wtyczek.
Możesz `zrezygnować` ustawiając tę zmienną na None.
Lub `zgodzić się` aby uzyskać więcej informacji ustawiając wartość na "full".
2019-07-24 16:50:36+0300 [-] Ustawianie bazy danych z URL 'sqlite:\/state.sqlite'
2019-07-24 16:50:36+0300 [-] ustawiając tryb dziennika bazy danych na 'wal'
2019-07-24 16:50:36+0300 [-] dodano 1 nową usługę, usunięto 0
2019-07-24 16:50:36+0300 [-] dodano 1 nowe źródło zmian, usunięto 0
2019-07-24 16:50:36+0300 [-] gitpoller: używanie katalogu roboczego '\/home\/buildbot\/master\/gitpoller-work'
2019-07-24 16:50:36+0300 [-] dodano 3 nowe budownicze, usunięto 0
2019-07-24 16:50:36+0300 [-] dodano 1 nowy harmonogram, usunięto 0
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'waterfall_view'
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'console_view'
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'grid_view'
2019-07-24 16:50:36+0300 [-] UWAGA: wtyczka www 'sitenav' jest zainstalowana, ale nie skonfigurowana
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'waterfall_view'
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'console_view'
2019-07-24 16:50:36+0300 [-] inicjalizowanie wtyczki www 'grid_view'
2019-07-24 16:50:36+0300 [-] UWAGA: wtyczka www 'sitenav' jest zainstalowana, ale nie skonfigurowana
2019-07-24 16:50:36+0300 [-] BuildbotSite uruchamia się na porcie 80
2019-07-24 16:50:36+0300 [-] Rozpoczynanie fabryki
2019-07-24 16:50:36+0300 [-] dodano 3 nowych pracowników, usunięto 0
2019-07-24 16:50:36+0300 [-] PBServerFactory uruchamia się na porcie 4000
2019-07-24 16:50:36+0300 [-] Rozpoczynanie fabryki
2019-07-24 16:50:37+0300 [-] BuildMaster działa
2019-07-24 16:50:37+0300 [-] buildbotNetUsageData: wysyłanie {'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] pracownik 'CentOS' przymocowany z IPv4Address(type='TCP', host='127.0.0.1', port=37332)
2019-07-24 16:50:37+0300 [Broker,0,127.0.0.1] Otrzymano informacje o pracowniku od 'CentOS'
2019-07-24 16:50:37+0300 [-] bot przymocowany
2019-07-24 16:50:37+0300 [Broker,0,127.0.0.1] Pracownik CentOS przymocowany do Rac-GUI-RPM-builder
2019-07-24 16:50:37+0300 [-] buildbotNetUsageData: buildbot.net powiedział: ok
2019-07-24 16:50:39+0300 [Broker,1,192.168.55.15] pracownik 'Windows-10' przymocowany z IPv4Address(type='TCP', host='192.168.5.145', port=49831)
2019-07-24 16:50:39+0300 [Broker,1,192.168.55.15] Otrzymano informacje o pracowniku od 'Windows-10'
2019-07-24 16:50:40+0300 [-] bot przymocowany
2019-07-24 16:50:40+0300 [Broker,1,192.168.55.15] Pracownik Windows-10 przymocowany do Rac-GUI-WIN-builder
2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] pracownik 'Debian-10' przymocowany z IPv4Address(type='TCP', host='192.168.5.9', port=44430)
2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] Otrzymano informacje o pracowniku od 'Debian-10'
2019-07-24 16:50:41+0300 [-] bot przymocowany
2019-07-24 16:50:41+0300 [Broker,2,192.168.55.99] Pracownik Debian-10 przymocowany do Rac-GUI-DEB-builderProces konfiguracyjny został zakończony. Można sprawdzić aktualny status przez interfejs webowy, gdzie można także zobaczyć błędy kompilacji, wznowić zawieszony proces, jeśli coś poszło nie tak itp.
Bezpośrednio po uruchomieniu naszych pracowników można sprawdzić przez menu „Kompilacje” -> „Pracownicy”

Po pierwszym procesie kompilacji (tj. zmianach w repozytorium Git) na pierwszej stronie pojawi się status procesów.

Jeśli klikniesz myszką na odpowiedni wiersz, otworzy się strona z aktualnym stanem danego procesu, gdzie widać, co się dzieje, jakie błędy itp.

Całą konfigurację magicznego narzędzia można znaleźć tutaj
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
))
# Usługa uruchamiająca kompilację
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)
)
# Dodamy określenie wersji z podstawowego pliku
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"
)
)
# Dodamy określenie wydania z podstawowego pliku
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'
))
# Skopiujemy plik na mastera
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)
)
# Dodamy określenie wersji z podstawowego pliku
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"
)
)
# Dodamy określenie wydania z podstawowego pliku
rac_gui_build_DEB.addStep(
steps.SetPropertyFromCommand(
command="grep release rac_gui.tcl | grep -oE 'b[0-9]{1,3}b'", property="release"
)
)
# Zmienimy nazwę pliku uruchamiającego
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["mv", "rac_gui.tcl", "racgui"]))
# Zmienimy ścieżki do bibliotek
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["sed", "-i", "s+^set dir(lib)+set dir(lib) /usr/share/rac-gui/lib ;#+g", "racgui"]))
# Zmienimy ścieżki do plików
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["sed", "-i", "s+[pwd]+/usr/share/rac-gui+g", "racgui"]))
# Spakujemy źródła
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["tar", "czf", util.Interpolate("../rac-gui_%(prop:version)s.orig.tar.gz"), "."]))
# Zbudujemy pakiet
rac_gui_build_DEB.addStep(steps.ShellCommand(
command=["dpkg-buildpackage"]))
# Skopiujemy plik na mastera
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)
)
# Dodamy określenie wersji z podstawowego pliku
rac_gui_build_WIN.addStep(
steps.SetPropertyFromCommand(
command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'version:') -split 's')[2]",
property="version"
)
)
# Dodamy określenie wydania z podstawowego pliku
rac_gui_build_WIN.addStep(
steps.SetPropertyFromCommand(
command="powershell -command "((Get-Content .rac_gui.tcl | Select-String -Pattern 'release:') -split 's')[2]",
property="release"
)
)
# Stworzymy plik wykonywalny
rac_gui_build_WIN.addStep(steps.ShellCommand(
command=["c:binfreewrap.exe", "rac_gui.tcl"]))
# Spakujemy, co udało się uzyskać
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"]))
# Skopiujemy na mastera
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")
)
)
# Skopiujemy plik 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>Status budowy: {{ summary }}</h4>
<p> Użyty pracownik: {{ workername }}</p>
{% for step in build['steps'] %}
<p> {{ step['name'] }}: {{ step['result'] }}</p>
{% endfor %}
<p><b> -- Buildbot</b></p>
'''
mailNotifier = reporters.MailNotifier(fromaddr="112@icvibor.pl",
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 budowania"
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"
}Materiały
Przy przygotowywaniu artykułu wykorzystano następujące materiały:
- No i dokumentacja produktu
Źródło: habr.com
