X-Git-Url: https://git.subdivi.de/?p=~helmut%2Fcrossqa.git;a=blobdiff_plain;f=webapp.py;h=cc9de3715279aa6c87c5968ac24eb82ef5c2de68;hp=6d1ad76f3c617e952d0c7568fe1a2f1bfa142107;hb=a52d12012b1befdf1f00d0fcd5101306c52271ff;hpb=330fe08852dd954f60f17fa04a06d00d3a15be74 diff --git a/webapp.py b/webapp.py index 6d1ad76..cc9de37 100644 --- a/webapp.py +++ b/webapp.py @@ -35,9 +35,11 @@ index_template = """ source version - architecture + build architecture + host architecture started result log + bugs @@ -49,7 +51,8 @@ index_template = """ {{ build.version|e }} - {{ build.architecture|e }} + {{ build.buildarch|e }} + {{ build.hostarch|e }} {{- build.starttime|sqltimestamp|formatts -}} @@ -57,6 +60,13 @@ index_template = """ log xz + + {%- if build.buglvl == 2 -%} + patch reported + {%- elif build.buglvl == 1 -%} + bug reported + {%- endif -%} + {%- endfor -%} @@ -73,7 +83,19 @@ index_template = """ """ -src_template = """ +src_template = """ +{%- macro render_bug(bugobj) -%} + #{{ bugobj.bugnum }} + {%- if bugobj.patched %} + [+] + {%- endif -%}: + {% if bugobj.package != "src:" + bugobj.affects and + not bugobj.title.startswith(bugobj.affects + ":") -%} + {{- bugobj.package|e }}: + {% endif -%} + {{- bugobj.title|e -}} +{%- endmacro -%} + {{ sourcepackage|e }} - Debian cross build @@ -87,13 +109,13 @@ tr.dep.tempbad td:nth-child(1) { tr.dep.good td:nth-child(1) { background-color: #afa; } -tr.build.bad td:nth-child(4) { +tr.build.bad td:nth-child(5) { background-color: #faa; } -tr.build.tempbad td:nth-child(4) { +tr.build.tempbad td:nth-child(5) { background-color: #ffa; } -tr.build.good td:nth-child(4) { +tr.build.good td:nth-child(5) { background-color: #afa; } th { @@ -121,8 +143,30 @@ footer { + {%- if bugs.ftbfs -%} +
+

Reported FTBFS bugs

+ +
+ {%- endif -%}

Cross build dependency satisfiability

+ {%- if bugs.bdsat -%} +
Reported satisfiability problems
+ + {%- endif -%} @@ -135,13 +179,13 @@ footer { {%- for reason, archs in depresult.items()|sort -%} - + {%- endfor -%} {%- if okarchs -%} - + {%- endif -%} @@ -162,13 +206,24 @@ footer {

Cross builds

+ {%- if bugs.ftcbfs -%} +
Reported cross build failures
+ + {%- endif -%} {%- if builds -%}
{{ reason|e }}{{ archs|arch_format }}{{ archs|archpairs_format }}
ok{{ okarchs|arch_format }}{{ okarchs|archpairs_format }}
- + + @@ -179,7 +234,8 @@ footer { {{- build.starttime|sqltimestamp|formatts -}} - + +
started versionarchitecturebuild architecturehost architecture result log
{{ build.version|e }}{{ build.architecture|e }}{{ build.buildarch|e }}{{ build.hostarch|e }} {{- "ok" if build.success else "failed" -}} @@ -197,10 +253,12 @@ footer { {{ sourcepackage|e }} for - + + {%- for buildarch, hostarch in architectures|sort -%} + {%- endfor -%} @@ -256,36 +314,67 @@ def formatts_filter(ts): return jinja2.Markup('' % (ts, ts, formatts(ts))) -@app.template_filter('arch_format') -@jinja2.contextfilter -def arch_format_filter(context, some_archs): - if context["architectures"] == some_archs: +@app.template_filter("archpair_format") +def archpair_format_filter(archpair): + return jinja2.Markup("%s → %s" % tuple(map(jinja2.escape, archpair))) + +def group_pairs(pairs): + result = {} + for v, w in pairs: + result.setdefault(v, set()).add(w) + return result + +def render_archset(subset, all_archs): + if len(subset) == 1: + return next(iter(subset)) + if subset == all_archs: return "any" - return ", ".join(sorted(some_archs)) + return "{%s}" % ", ".join(map(jinja2.escape, sorted(subset))) + +@app.template_filter('archpairs_format') +@jinja2.contextfilter +def archpairs_format_filter(context, some_archs): + architectures = group_pairs(context["architectures"]) + fwdmap = {} # build architecture -> host architecture set representation + for buildarch, hostarchs in group_pairs(some_archs).items(): + fwdmap[buildarch] = render_archset(hostarchs, architectures[buildarch]) + allbuildarchs = set(architectures.keys()) + # host architecture set representation -> build architecture set + flippedit = group_pairs((v, k) for (k, v) in fwdmap.items()).items() + maps = ("%s → %s" % (render_archset(buildarchs, allbuildarchs), + hostarchrep) + for hostarchrep, buildarchs in flippedit) + return jinja2.Markup("; ".join(sorted(maps))) def collect_depstate(conn, source): version = None depstate = None query = sqlalchemy.text(""" - SELECT version, architecture, satisfiable, reason + SELECT version, 'amd64' AS buildarch, architecture AS hostarch, + 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 + depstate[row.buildarch, row.hostarch] = \ + 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) + for archpair, reason in depstate.items(): + depresult.setdefault(reason, set()).add(archpair) return version, depresult @app.route("/") def show_index(): with db.engine.connect() as conn: builds = list(conn.execute(""" - SELECT source, version, architecture, starttime, filename + SELECT source, version, 'amd64' AS buildarch, + architecture AS hostarch, starttime, filename, + ifnull((SELECT max(patched + 1) FROM bugs + WHERE affects = source), + 0) AS buglvl FROM builds WHERE success = 0 ORDER BY starttime @@ -296,14 +385,23 @@ def show_index(): 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)) + query = sqlalchemy.text("""SELECT 'amd64' AS buildarch, + architecture AS hostarch + FROM depcheck;""") + context["architectures"] = set(map(tuple, conn.execute(query))) context["version"], context["depresult"] = collect_depstate(conn, source) query = sqlalchemy.text(""" - SELECT version, architecture, success, starttime, filename + SELECT version, 'amd64' AS buildarch, architecture AS hostarch, + success, starttime, filename FROM builds WHERE source = :source;""") context["builds"] = list(conn.execute(query, source=source)) + query = sqlalchemy.text(""" + SELECT bugnum, kind, title, package, patched, affects + FROM bugs WHERE affects = :affects;""") + context["bugs"] = {} + for bug in conn.execute(query, affects=source): + context["bugs"].setdefault(bug.kind, []).append(bug) context["show_bootstrapdn"] = \ any(reason and not reason.startswith("skew ") for reason in context["depresult"].keys()) @@ -327,21 +425,23 @@ def show_log(filename): @app.route("/schedule", methods=["POST"]) def request_schedule(): source = flask.request.form["source"] - architecture = flask.request.form["architecture"] + buildarch, hostarch = flask.request.form["archpair"].split("_") + if buildarch not in ("any", "amd64"): + raise werkzeug.exceptions.BadRequest() 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 + if hostarch == "any": + hostarch = None else: query = sqlalchemy.text(""" - SELECT 1 FROM depcheck WHERE architecture = :architecture;""") - if not conn.execute(query, architecture=architecture).first(): + SELECT 1 FROM depcheck WHERE architecture = :hostarch;""") + if not conn.execute(query, hostarch=hostarch).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) + VALUES (:source, :hostarch, datetime('now'));""") + conn.execute(query, source=source, hostarch=hostarch) return flask.render_template_string(schedule_template)