#!/usr/bin/python3 # SPDX-License-Identifier: GPL-2.0+ import argparse import collections import contextlib import datetime import lzma import os.path import sqlite3 import subprocess import time from common import decompress_stream, yield_lines, yield_chunks def scan_log_status(filelike): it = yield_chunks(filelike) it = decompress_stream(it, lzma.LZMADecompressor()) it = yield_lines(it) last_lines = collections.deque(it, 25) status = [l.split(b':', 1)[1].strip() for l in last_lines if l.startswith(b"Status:")] if status: return status[0].decode("ascii") return "unknown" def do_build(source, version, architecture, server): now = datetime.datetime.utcnow().replace(microsecond=0) logtarget = "%s_%s_%s_%s.log.xz" % (source, version, architecture, now.strftime("%Y%m%d%H%M%S")) cmdline = ["ssh", server, "sh", "/dev/stdin", architecture, "%s_%s" % (source, version)] with open(os.path.join("logs", logtarget), "w+b") as output: with open("build.sh", "rb") as instructions: code = subprocess.call(cmdline, stdin=instructions, stdout=output) output.seek(0) status = scan_log_status(output) print("status %s code %d" % (status, code)) return (now, code == 0, logtarget, status == "given-back") def main(): argp = argparse.ArgumentParser() argp.add_argument("server", help="machine to build on") args = argp.parse_args() db = sqlite3.connect("db", detect_types=sqlite3.PARSE_DECLTYPES) with contextlib.closing(db.cursor()) as cur: cur.execute(""" SELECT d.source, d.version, d.architecture, r.rowid FROM depstate AS d JOIN buildrequests AS r ON d.architecture = ifnull(r.architecture, d.architecture) AND d.source = r.source JOIN depcheck ON d.architecture = depcheck.architecture WHERE d.satisfiable = 1 AND depcheck.giveback = 0 ORDER BY r.priority DESC, r.requesttime ASC LIMIT 1;""") row = cur.fetchone() if not row: cur.execute(""" SELECT source, version, depstate.architecture, NULL FROM depstate JOIN depcheck ON depstate.architecture = depcheck.architecture WHERE satisfiable = 1 AND giveback = 0 ORDER BY random() LIMIT 1;""") row = cur.fetchone() if not row: print("no package satisfiable") time.sleep(60) return source, version, architecture, requestid = row print("building %s_%s for %s%s" % (source, version, architecture, "" if requestid is None else " (request %d)" % requestid)) timestamp, success, filename, giveback = \ do_build(source, version, architecture, args.server) with contextlib.closing(db.cursor()) as cur: cur.execute("INSERT INTO builds (source, version, architecture, success, starttime, filename) VALUES (?, ?, ?, ?, ?, ?);", (source, version, architecture, success, timestamp, filename)) if requestid is not None: cur.execute("DELETE FROM buildrequests WHERE rowid = ?;", (requestid,)) if giveback: cur.execute("UPDATE depcheck SET giveback = 1 WHERE architecture = ?;", (architecture,)) db.commit() if __name__ == "__main__": main()