webapp: get rid of the annoying sqlalchemy warning
[~helmut/crossqa.git] / build.py
1 #!/usr/bin/python3
2
3 import argparse
4 import collections
5 import contextlib
6 import datetime
7 import lzma
8 import os.path
9 import sqlite3
10 import subprocess
11 import time
12
13 from common import decompress_stream, yield_lines, yield_chunks
14
15 def scan_log_status(filelike):
16     it = yield_chunks(filelike)
17     it = decompress_stream(it, lzma.LZMADecompressor())
18     it = yield_lines(it)
19     last_lines = collections.deque(it, 25)
20     status = [l.split(b':', 1)[1].strip()
21               for l in last_lines if l.startswith(b"Status:")]
22     if status:
23         return status[0].decode("ascii")
24     return "unknown"
25
26
27 def do_build(source, version, architecture, server):
28     now = datetime.datetime.utcnow().replace(microsecond=0)
29     logtarget = "%s_%s_%s_%s.log.xz" % (source, version, architecture,
30                                         now.strftime("%Y%m%d%H%M%S"))
31     cmdline = ["ssh", server, "sh", "/dev/stdin", architecture,
32                "%s_%s" % (source, version)]
33     with open(os.path.join("logs", logtarget), "w+b") as output:
34         with open("build.sh", "rb") as instructions:
35             code = subprocess.call(cmdline, stdin=instructions, stdout=output)
36         output.seek(0)
37         status = scan_log_status(output)
38         print("status %s code %d" % (status, code))
39     return (now, code == 0, logtarget, status == "given-back")
40
41 def main():
42     argp = argparse.ArgumentParser()
43     argp.add_argument("server", help="machine to build on")
44     args = argp.parse_args()
45     db = sqlite3.connect("db", detect_types=sqlite3.PARSE_DECLTYPES)
46     with contextlib.closing(db.cursor()) as cur:
47         cur.execute("SELECT source, version, depstate.architecture FROM depstate JOIN depcheck ON depstate.architecture = depcheck.architecture WHERE satisfiable = 1 AND giveback = 0 ORDER BY random() LIMIT 1;")
48         row = cur.fetchone()
49     if not row:
50         print("no package satisfiable")
51         time.sleep(60)
52         return
53     source, version, architecture = row
54     print("building %s_%s for %s" % (source, version, architecture))
55     timestamp, success, filename, giveback = \
56         do_build(source, version, architecture, args.server)
57     with contextlib.closing(db.cursor()) as cur:
58         cur.execute("INSERT INTO builds (source, version, architecture, success, starttime, filename) VALUES (?, ?, ?, ?, ?, ?);",
59                     (source, version, architecture, success, timestamp,
60                      filename))
61         if giveback:
62             cur.execute("UPDATE depcheck SET giveback = 1 WHERE architecture = ?;",
63                         (architecture,))
64     db.commit()
65
66 if __name__ == "__main__":
67     main()