mirror of
https://github.com/certbot/certbot.git
synced 2026-06-06 23:32:06 -04:00
Nice UI
This commit is contained in:
parent
877632326d
commit
1d36f53bd3
1 changed files with 80 additions and 44 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue