This commit is contained in:
Adrien Ferrand 2020-07-17 14:14:26 +02:00
parent 877632326d
commit 1d36f53bd3

View file

@ -1,18 +1,22 @@
#!/usr/bin/env python3
import argparse
import curses
import glob
import multiprocessing
import datetime
from multiprocessing import Pool, Process, Manager
import re
import subprocess
import sys
from os.path import join, realpath, dirname, basename, exists
import time
from os.path import join, realpath, dirname, basename
CERTBOT_DIR = dirname(dirname(dirname(realpath(__file__))))
PLUGINS = [basename(path) for path in glob.glob(join(CERTBOT_DIR, 'certbot-dns-*'))]
def _build_snap(target, archs):
def _build_snap(target, archs, status):
status[target] = {}
if target == 'certbot':
workspace = CERTBOT_DIR
else:
@ -22,55 +26,81 @@ def _build_snap(target, archs):
'| grep -v python-augeas > "{1}/snap-constraints.txt"').format(sys.executable, workspace),
shell=True, cwd=CERTBOT_DIR)
process = subprocess.Popen([
'snapcraft', 'remote-build', '--launchpad-accept-public-upload', '--recover', '--build-on', ','.join(archs)
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, cwd=workspace)
line = process.stdout.readline()
while line:
_extract_state(target, line, status)
line = process.stdout.readline()
return {target: workspace}
def _extract_state(project, output, status):
match = re.match(r'^.*arch=(\w+)\s+state=([\w ]+).*$', output)
if match:
arch = match.group(1)
state_str = match.group(2)
state = status[project]
if state_str == 'Successfully built':
state[arch] = 'S'
elif state_str == 'Failed to build':
state[arch] = 'F'
elif state_str == 'Uploading build':
state[arch] = 'U'
elif state_str == 'Currently building':
state[arch] = 'B'
elif state_str == 'Needs building':
state[arch] = 'W'
status[project] = state
def _dump_status(status):
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
try:
subprocess.check_call([
'snapcraft', 'remote-build', '--launchpad-accept-public-upload',
'--build-on', ','.join(archs)
], universal_newlines=True, cwd=workspace)
except subprocess.CalledProcessError as e:
# Will be handled after
pass
while True:
stdscr.addstr(0, 0, 'Build status at {0}'.format(datetime.datetime.now()))
stdscr.addstr(1, 0, 'W = wait, B = building, U = uploading, F = fail, S = success')
stdscr.addstr(2, 0, ' project amd64 arm64 armhf ')
stdscr.addstr(3, 0, '---------------------------+-------+-------+-------')
idx = 4
for project, states in status.items():
stdscr.addstr(idx, 0, ' {0} | {1} | {2} | {3} '.format(
project + ' ' * (25 - len(project)), states.get('arm64', 'W'),
states.get('arm64', 'W'), states.get('armhf', 'W')))
idx = idx + 1
status = {}
for arch in archs:
status[arch] = None
build_file = join(workspace, '{0}_{1}.txt'.format(target, arch))
if exists(build_file):
with open(build_file) as file_h:
build_output = file_h.read()
if not re.search(r'Snapped {0}_.*_{1}\.snap'.format(target, arch), build_output):
status[arch] = build_output
return {target: status}
stdscr.refresh()
time.sleep(1)
finally:
curses.echo()
curses.nocbreak()
curses.endwin()
def _dump_results(targets, archs, results):
def _dump_results(targets, archs, status, workspaces):
failures = False
for target in targets:
for arch in archs:
build_output = results[target][arch]
if build_output:
result = status[target][arch]
if result == 'F':
failures = True
with open(join(workspaces[target], '{0}_{1}.txt'.format(target, arch))) as file_h:
build_output = file_h.read()
print('Output for failed build target={0} arch={1}'.format(target, arch))
print('-------------------------------------------')
print(build_output)
print('-------------------------------------------')
print('Build summary')
print('=============')
targets = list(targets)
targets.sort()
for target in targets:
print('Builds for target={0}: {1}'.format(
target,
', '.join([
'arch={0} ({1})'.format(arch, 'success' if not results[target][arch] else 'failure')
for arch in archs
])
))
return failures
@ -93,15 +123,21 @@ def main():
targets.remove('DNS_PLUGINS')
targets.update(PLUGINS)
pool = multiprocessing.Pool(processes=len(targets))
async_results = [pool.apply_async(_build_snap, (target, archs)) for target in targets]
status = Manager().dict()
results = {}
state_process = Process(target=_dump_status, args=(status,))
state_process.start()
pool = Pool(processes=len(targets))
async_results = [pool.apply_async(_build_snap, (target, archs, status)) for target in targets]
workspaces = {}
for async_result in async_results:
results.update(async_result.get())
workspaces.update(async_result.get())
failures = _dump_results(targets, archs, results)
state_process.terminate()
failures = _dump_results(targets, archs, status, workspaces)
return 1 if failures else 0