#!/usr/bin/python3 import datetime import lzma import apt_pkg apt_pkg.init() version_compare = apt_pkg.version_compare import flask import flask_sqlalchemy import jinja2 import sqlalchemy import werkzeug app = flask.Flask("crossqa") app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = flask_sqlalchemy.SQLAlchemy(app) src_template = """ {{ sourcepackage|e }} - Debian cross build

{{ sourcepackage|e }}

Cross satisfiability

{%- set okarchs = depresult.pop(None, None) -%} {%- for reason, archs in depresult.items()|sort -%} {%- endfor -%} {%- if okarchs -%} {%- endif -%}
state architectures
{{ reason|e }} {{ archs|arch_format }}
ok {{ okarchs|arch_format }}
See also
{%- if builds -%}

Cross builds

{%- for build in builds|sort(attribute='starttime', reverse=true) -%} {%- endfor -%}
started version architecture result
{{- build.starttime|sqltimestamp|formatts -}} {{ build.version|e }} {{ build.architecture|e }} {{- "ok" if build.success else "failed" -}} xz
{%- endif -%} """ @app.template_filter("sqltimestamp") def sqltimestamp_filter(s): try: return datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S.%f") except ValueError: return datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S") @app.template_filter("formatts") def formatts_filter(ts): assert isinstance(ts, datetime.datetime) dt = datetime.datetime.utcnow() - ts if dt < datetime.timedelta(seconds=1): return "now" if dt < datetime.timedelta(seconds=100): return "%d s" % dt.seconds if dt < datetime.timedelta(minutes=100): return "%d m" % (dt.seconds // 60) if dt < datetime.timedelta(days=1): return "%d h" % (dt.seconds // (60 * 60)) return "%d d" % dt.days @app.template_filter('arch_format') @jinja2.contextfilter def arch_format_filter(context, some_archs): if context["architectures"] == some_archs: return "any" return ", ".join(sorted(some_archs)) def collect_depstate(conn, source): version = None depstate = None query = sqlalchemy.text(""" SELECT version, architecture, satisfiable, reason FROM depstate WHERE source = :source;""") for row in conn.execute(query, source=source): if version is None or version_compare(version, row.version) > 0: version = row.version depstate = {} depstate[row.architecture] = None if row.satisfiable else row.reason if version is None: raise werkzeug.exceptions.NotFound() depresult = {} for arch, reason in depstate.items(): depresult.setdefault(reason, set()).add(arch) return version, depresult @app.route("/src/") def show_source(source): context = dict(sourcepackage=source) with db.engine.connect() as conn: query = sqlalchemy.text("SELECT architecture FROM depcheck;") context["architectures"] = set(row[0] for row in conn.execute(query)) context["version"], context["depresult"] = collect_depstate(conn, source) query = sqlalchemy.text(""" SELECT version, architecture, success, starttime, filename FROM builds WHERE source = :source;""") context["builds"] = list(conn.execute(query, source=source)) context["show_bootstrapdn"] = \ any(reason and not reason.startswith("skew ") for reason in context["depresult"].keys()) return flask.render_template_string(src_template, **context) @app.route("/build/") def show_log(filename): if filename.endswith(".xz"): return flask.send_from_directory("logs", filename, mimetype="application/octet-stream") filename += ".xz" return flask.send_file(lzma.open(flask.safe_join("logs", filename), "rb"), mimetype="text/plain")