depcheck: add python3.9-minimal to bad_foreign_packages
[~helmut/crossqa.git] / build.py
1 #!/usr/bin/python3
2 # SPDX-License-Identifier: GPL-2.0+
3
4 import argparse
5 import collections
6 import contextlib
7 import datetime
8 import lzma
9 import os.path
10 import sqlite3
11 import subprocess
12 import time
13
14 from common import decompress_stream, yield_lines, yield_chunks
15
16 def scan_log_status(filelike):
17     it = yield_chunks(filelike)
18     it = decompress_stream(it, lzma.LZMADecompressor())
19     it = yield_lines(it)
20     last_lines = collections.deque(it, 25)
21     status = [l.split(b':', 1)[1].strip()
22               for l in last_lines if l.startswith(b"Status:")]
23     if status:
24         return status[0].decode("ascii")
25     return "unknown"
26
27
28 def do_build(source, version, architecture, server):
29     now = datetime.datetime.utcnow().replace(microsecond=0)
30     logtarget = "%s_%s_%s_%s.log.xz" % (source, version, architecture,
31                                         now.strftime("%Y%m%d%H%M%S"))
32     cmdline = ["ssh", server, "sh", "/dev/stdin", architecture,
33                "%s_%s" % (source, version)]
34     logname = os.path.join("logs", logtarget)
35     with open(logname, "w+b") as output:
36         with open("build.sh", "rb") as instructions:
37             code = subprocess.call(cmdline, stdin=instructions, stdout=output)
38         output.seek(0)
39         status = scan_log_status(output)
40     if code == 255:
41         os.unlink(logname)
42         time.sleep(300)
43         raise RuntimeError("ssh failed")
44     print("status %s code %d" % (status, code))
45     return (now, code == 0, logtarget, status == "given-back")
46
47 def main():
48     argp = argparse.ArgumentParser()
49     argp.add_argument("server", help="machine to build on")
50     args = argp.parse_args()
51     db = sqlite3.connect("db", detect_types=sqlite3.PARSE_DECLTYPES)
52     with contextlib.closing(db.cursor()) as cur:
53         cur.execute("""
54             SELECT d.source, d.version, d.architecture, r.id
55                 FROM depstate AS d
56                     JOIN buildrequests AS r
57                         ON d.architecture = ifnull(r.architecture, d.architecture)
58                             AND d.source = r.source
59                     JOIN depcheck
60                         ON d.architecture = depcheck.architecture
61                 WHERE d.satisfiable = 1 AND depcheck.giveback = 0
62                 ORDER BY r.priority DESC, r.requesttime ASC, random()
63                 LIMIT 1;""")
64         row = cur.fetchone()
65         if not row:
66             cur.execute("""
67                 SELECT source, version, depstate.architecture, NULL
68                     FROM depstate JOIN depcheck
69                         ON depstate.architecture = depcheck.architecture
70                     WHERE satisfiable = 1 AND giveback = 0
71                     ORDER BY random() LIMIT 1;""")
72             row = cur.fetchone()
73     if not row:
74         print("no package satisfiable")
75         time.sleep(60)
76         return
77     source, version, architecture, requestid = row
78     print("building %s_%s for %s%s" %
79           (source, version, architecture,
80            "" if requestid is None else " (request %d)" % requestid))
81     timestamp, success, filename, giveback = \
82         do_build(source, version, architecture, args.server)
83     with contextlib.closing(db.cursor()) as cur:
84         cur.execute("INSERT INTO builds (source, version, architecture, success, starttime, filename) VALUES (?, ?, ?, ?, ?, ?);",
85                     (source, version, architecture, success, timestamp,
86                      filename))
87         if requestid is not None:
88             cur.execute("DELETE FROM buildrequests WHERE id = ?;",
89                         (requestid,))
90         if giveback:
91             cur.execute("UPDATE depcheck SET giveback = 1 WHERE architecture = ?;",
92                         (architecture,))
93     db.commit()
94
95 if __name__ == "__main__":
96     main()