diff options
author | Helmut Grohne <helmut@subdivi.de> | 2019-03-23 10:06:16 +0100 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2019-03-23 10:06:16 +0100 |
commit | 09e5bba31b864da3e86b2747da9bc6cfebe90cbe (patch) | |
tree | 9b998fa0a54cf92783757624b2c950ffc829b90f | |
parent | 2153cf032b8690aff87a601bdb0e48bf25a5247a (diff) | |
download | crossqa-09e5bba31b864da3e86b2747da9bc6cfebe90cbe.tar.gz |
allow scheduling builds via webapp
-rwxr-xr-x | build.py | 28 | ||||
-rw-r--r-- | schema.sql | 6 | ||||
-rw-r--r-- | webapp.py | 58 |
3 files changed, 84 insertions, 8 deletions
@@ -45,20 +45,42 @@ def main(): args = argp.parse_args() db = sqlite3.connect("db", detect_types=sqlite3.PARSE_DECLTYPES) with contextlib.closing(db.cursor()) as cur: - 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;") + 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, random() 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 = row - print("building %s_%s for %s" % (source, version, architecture)) + 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,)) @@ -30,3 +30,9 @@ CREATE TABLE builds ( success BOOLEAN NOT NULL CHECK (success in (0, 1)), starttime TIMESTAMP NOT NULL, filename TEXT NOT NULL); + +CREATE TABLE buildrequests ( + source TEXT NOT NULL, + architecture TEXT, + requesttime TIMESTAMP NOT NULL, + priority INTEGER NOT NULL DEFAULT 0); @@ -104,9 +104,9 @@ footer { </li> </ul> </section> - {%- if builds -%} - <section> - <h3>Cross builds</h3> + <section> + <h3>Cross builds</h3> + {%- if builds -%} <table> <thead> <tr> @@ -134,8 +134,21 @@ footer { {%- endfor -%} </tbody> </table> - </section> - {%- endif -%} + {%- else -%} + <p>No build performed yet.</p> + {%- endif -%} + <form method="POST" action="{{ url_for("request_schedule")|e }}"> + <input type="submit" name="schedule" value="cross build" /> + {{ sourcepackage|e }} for + <input type="hidden" name="source" value="{{ sourcepackage|e }}" /> + <select name="architecture"> + <option value="any">any</option> + {%- for architecture in architectures -%} + <option value="{{ architecture|e }}">{{ architecture|e }}</option> + {%- endfor -%} + </select> + </form> + </section> <footer> <h3>Details about this service</h3> <ul> @@ -147,6 +160,18 @@ footer { </html> """ +schedule_template = """<!DOCTYPE html> +<html> + <body> + <p>Scheduled a build of {{ request.form["source"]|e }} + {%- if request.form["architecture"] != "any" %} + for {{ request.form["architecture"]|e -}} + {%- endif %}. + <p> + </body> +</html> +""" + @app.template_filter("sqltimestamp") def sqltimestamp_filter(s): strptime = datetime.datetime.strptime @@ -228,3 +253,26 @@ def show_log(filename): mimetype="text/plain") except FileNotFoundError: raise werkzeug.exceptions.NotFound() + + +@app.route("/schedule", methods=["POST"]) +def request_schedule(): + source = flask.request.form["source"] + architecture = flask.request.form["architecture"] + with db.engine.connect() as conn: + query = sqlalchemy.text(""" + SELECT 1 FROM depstate WHERE source = :source;""") + if not conn.execute(query, source=source).first(): + raise werkzeug.exceptions.BadRequest() + if architecture == "any": + architecture = None + else: + query = sqlalchemy.text(""" + SELECT 1 FROM depcheck WHERE architecture = :architecture;""") + if not conn.execute(query, architecture=architecture).first(): + raise werkzeug.exceptions.BadRequest() + query = sqlalchemy.text(""" + INSERT INTO buildrequests (source, architecture, requesttime) + VALUES (:source, :architecture, datetime('now'));""") + conn.execute(query, source=source, architecture=architecture) + return flask.render_template_string(schedule_template) |