amber-backend/pb_migrations/1796000000_e2e_runs.js

76 lines
3.2 KiB
JavaScript
Raw Normal View History

/// <reference path="../pb_data/types.d.ts" />
// What the release gate found, run by run.
//
// The catalogue in amber-app (`docs/e2e/catalogue.yaml`) says what a person must
// be able to do before a build goes out; the runner executes it and writes one
// record here per run. The dashboard's Testy tab reads them.
//
// WHY A COLLECTION AND NOT A SERVICE: the owner's call, and the right one. A
// whole application to display a table of results would cost more to keep alive
// than the thing it displays, and the VPS's disk is already the scarce resource
// (old releases eat it, not databases).
//
// **The screenshots live here too, for as long as the run does.** A result whose
// evidence has been deleted is a claim again rather than a record, and they are
// small next to a release artifact. Deleting a run takes its evidence with it,
// which is the retention policy in one sentence.
//
// Superuser-only, like the status dashboard it appears in: this is operator
// data, it names internal ids and it carries screenshots of a signed-in app.
// There is no reason for a family account to be able to read it.
migrate(
(app) => {
const runs = new Collection({
type: "base",
name: "e2e_runs",
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
fields: [
// The run's own stamp, e.g. 2026-09-10T00-31-02Z — the directory name on
// the machine that ran it, so a record can be traced back to its files.
{ type: "text", name: "run", required: true, max: 40 },
// linux | windows | phone | tv | attach
{ type: "text", name: "target", required: true, max: 20 },
// What was under test, not what wrote the record: the app's version and
// the commit the cases came from. Without both, a green run a month old
// says nothing about which build it green-lit.
{ type: "text", name: "appVersion", required: true, max: 40 },
{ type: "text", name: "commit", required: true, max: 40 },
{ type: "number", name: "catalogueVersion", onlyInt: true },
// { pass, fail, blocked, unsupported, unimplemented }
{ type: "json", name: "counts", maxSize: 2000 },
// One entry per case: id, outcome, blocker, how, ms, evidence, error.
// The evidence entries name their screenshot files, which is how the
// dashboard pairs a claim with the picture that proves it.
{ type: "json", name: "cases", maxSize: 2000000 },
{
type: "file",
name: "shots",
maxSelect: 200,
maxSize: 8388608,
mimeTypes: ["image/png"],
},
// Anything the run wants to say for itself — usually why a target could
// not start at all, which is a result worth keeping rather than an
// absence of one.
{ type: "text", name: "note", max: 2000 },
{ type: "autodate", name: "created", onCreate: true },
],
indexes: [
"CREATE INDEX idx_e2e_runs_created ON e2e_runs (created)",
"CREATE INDEX idx_e2e_runs_target ON e2e_runs (target, created)",
],
});
app.save(runs);
},
(app) => {
const c = app.findCollectionByNameOrId("e2e_runs");
if (c) app.delete(c);
},
);