diff --git a/assets/sai.css b/assets/sai.css
index ea28791..7075275 100644
--- a/assets/sai.css
+++ b/assets/sai.css
@@ -41,7 +41,7 @@ img.tread2 {
img.zup {
position: relative;
- z-index: 1001
+ z-index: 11
}
.tab label {
@@ -1413,3 +1413,82 @@ canvas.power-graph {
.taskstate6.prog-95 { background: linear-gradient(90deg, #df3030 95%, transparent 95%); }
.taskstate6.prog-100 { background: linear-gradient(90deg, #df3030 100%, transparent 100%); }
+.taskstate.has_runs {
+ border: 2px solid black;
+ margin: -1px;
+ position: relative;
+ z-index: 10;
+}
+
+.taskstate.has_runs:hover {
+ z-index: 1001;
+ opacity: 1.0 !important;
+}
+
+.runs-popup {
+ display: none;
+ position: absolute;
+ background: #ccc;
+ border: 1px solid black;
+ padding: 5px;
+ z-index: 2000;
+ opacity: 1.0 !important;
+ box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
+}
+
+.taskstate:hover {
+ opacity: 1.0 !important;
+}
+
+.taskstate:hover .runs-popup {
+ display: block;
+}
+
+.runs-popup table {
+ border-collapse: collapse;
+}
+
+.runs-popup td {
+ padding: 2px 5px;
+ white-space: nowrap;
+}
+
+.runs-header-container {
+ margin-top: -8px;
+}
+
+.runs-table {
+ border-collapse: separate;
+ border-spacing: 4px 0px;
+}
+
+.runs-table td {
+ text-align: center;
+ padding: 0px 2px;
+}
+
+.runs-popup table {
+ border-collapse: collapse;
+}
+
+.runs-popup td {
+ padding: 2px 8px;
+ text-align: left;
+}
+
+.run-current-decal {
+ border: 2px solid #000;
+ position: relative;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.run-decal {
+ position: relative;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.runs-time-cell {
+ white-space: nowrap;
+}
diff --git a/assets/sai.js b/assets/sai.js
index ec2f829..8dac6a9 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1089,6 +1089,22 @@ function sai_taskinfo_render(t, now_ut)
sai_arts = "";
}
+ if (t.runs && t.runs.length > 1) {
+ var r1 = "", r2 = "";
+ s += "<div class=\"runs-header-container\"><table class=\"runs-table\"><tr>";
+ for (var n = t.runs.length - 1; n >= 0; n--) {
+ var r = t.runs[n];
+ var ridx = typeof r.run !== 'undefined' ? r.run : 0;
+ var current = (ridx == (typeof t.t.run !== 'undefined' ? t.t.run : 0));
+ var dcl = current ? "run-current-decal" : "run-decal";
+ var decal = "<div class=\"taskstate taskstate" + r.state + " " + dcl + "\"><a href=\"?task=" + t.t.uuid + "&run=" + ridx + "\">" + sai_plat_icon(r.platform, 0) + "</a></div>";
+ var timeStr = r.started ? agify(now_ut, r.started) + " ago" : "pending";
+ r1 += "<td>" + decal + "</td>";
+ r2 += "<td><span class=\"ti5\">" + timeStr + "</span></td>";
+ }
+ s += r1 + "</tr><tr>" + r2 + "</tr></table></div>";
+ }
+
s += "</td></tr>";
s += "</td></tr></table></table>";
@@ -1159,28 +1175,28 @@ function summarize_build_situation(event_uuid)
if (!roo)
return { text: "" };
- same = roo.querySelectorAll(".taskstate");
+ same = roo.querySelectorAll(".taskstate:not(.run-decal)");
if (same)
total = same.length;
- same = roo.querySelectorAll(".taskstate0");
+ same = roo.querySelectorAll(".taskstate0:not(.run-decal)");
if (same)
pending = same.length;
- same = roo.querySelectorAll(".taskstate1");
+ same = roo.querySelectorAll(".taskstate1:not(.run-decal)");
if (same)
ongoing += same.length;
- same = roo.querySelectorAll(".taskstate2");
+ same = roo.querySelectorAll(".taskstate2:not(.run-decal)");
if (same)
ongoing += same.length;
- same = roo.querySelectorAll(".taskstate3");
+ same = roo.querySelectorAll(".taskstate3:not(.run-decal)");
if (same)
good = same.length;
- same = roo.querySelectorAll(".taskstate4");
+ same = roo.querySelectorAll(".taskstate4:not(.run-decal)");
if (same)
bad += same.length;
- same = roo.querySelectorAll(".taskstate5");
+ same = roo.querySelectorAll(".taskstate5:not(.run-decal)");
if (same)
bad += same.length; // treat cancelled as bad
- same = roo.querySelectorAll(".taskstate6");
+ same = roo.querySelectorAll(".taskstate6:not(.run-decal)");
if (same)
ongoing += same.length;
@@ -1348,9 +1364,25 @@ function sai_event_render(o, now_ut, reset_all_icon)
s += "<table><tr><td class=\"atop\">";
+ var run_max = {}, run_list = {};
+ for (q = 0; q < o.t.length; q++) {
+ var tx = o.t[q];
+ var ru = typeof tx.run !== 'undefined' ? tx.run : 0;
+ if (!run_list[tx.uuid]) run_list[tx.uuid] = [];
+ run_list[tx.uuid].push(tx);
+ if (typeof run_max[tx.uuid] === 'undefined' || ru > (typeof run_max[tx.uuid].run !== 'undefined' ? run_max[tx.uuid].run : 0))
+ run_max[tx.uuid] = tx;
+ }
+ for (var uid in run_list) {
+ run_list[uid].sort(function(a, b) { var ar = typeof a.run !== 'undefined' ? a.run : 0; var br = typeof b.run !== 'undefined' ? b.run : 0; return ar - br; });
+ }
+
for (q = 0; q < o.t.length; q++) {
var t = o.t[q];
+ if (t !== run_max[t.uuid])
+ continue;
+
if (t.taskname !== ctn) {
if (ctn !== "") {
s += "<div class=\"ib\"><table class=\"nomar\">" +
@@ -1363,10 +1395,22 @@ function sai_event_render(o, now_ut, reset_all_icon)
}
s1 += "<div id=\"taskstate_" + t.uuid + "\" class=\"taskstate taskstate" + t.state +
+ (run_list[t.uuid].length > 1 ? " has_runs" : "") +
"\" data-event-uuid=\"" + san(e.uuid) + "\" data-platform=\"" + san(t.platform) +
"\" data-rebuildable=\"" + t.rebuildable + "\">";
s1 += "<a href=\"/sai/index.html?task=" + t.uuid + "\">" +
sai_plat_icon(t.platform, 0) + "</a>";
+ if (run_list[t.uuid].length > 1) {
+ s1 += "<div class=\"runs-popup\"><table>";
+ for (var w = 0; w < run_list[t.uuid].length; w++) {
+ var rt = run_list[t.uuid][w];
+ var rr = typeof rt.run !== 'undefined' ? rt.run : 0;
+ var decal = "<div class=\"taskstate taskstate" + rt.state + " run-decal\"><a href=\"/sai/index.html?task=" + t.uuid + "&run=" + rr + "\">" + sai_plat_icon(rt.platform, 0) + "</a></div>";
+ var timeStr = rt.started ? agify(now_ut, rt.started) + " ago" : "pending";
+ s1 += "<tr><td>" + decal + "</td><td class=\"runs-time-cell\"><span class=\"ti5\">" + timeStr + "</span></td></tr>";
+ }
+ s1 += "</table></div>";
+ }
s1 += "</div>";
}
@@ -1385,7 +1429,7 @@ function sai_event_render(o, now_ut, reset_all_icon)
s += "</tr>";
- return s;
+ return "<tbody id=\"ev-group-" + o.e.uuid + "\">" + s + "</tbody>";
}
function getBuilderHostname(platName) {
@@ -2032,10 +2076,10 @@ function ws_open_sai()
document.body.classList.remove("overlay-active");
var par = new URLSearchParams(window.location.search),
- tid, eid;
+ tid, eid, run_idx;
tid = par.get('task');
eid = par.get('event');
-
+ run_idx = par.get('run');
if (tid) {
/*
@@ -2046,13 +2090,17 @@ function ws_open_sai()
console.log("tid " + tid);
- sai.send("{\"schema\":" +
+ var req = "{\"schema\":" +
"\"com.warmcat.sai.taskinfo\"," +
"\"js_api_version\": " + SAI_JS_API_VERSION + "," +
"\"logs\": 1," +
- "\"last_log_ts\":" + last_log_timestamp + "," +
- "\"task_hash\":" +
- JSON.stringify(tid) + "}");
+ "\"last_log_ts\":" + last_log_timestamp + ",";
+ if (run_idx)
+ req += "\"run\":" + run_idx + ",";
+ else
+ req += "\"run\": -1,";
+ req += "\"task_hash\":" + JSON.stringify(tid) + "}";
+ sai.send(req);
return;
}
@@ -2302,15 +2350,16 @@ function ws_open_sai()
// console.log("jso.overview.length " + jso.overview.length);
if (jso.overview.length == 1 &&
- document.getElementById("esr-" + jso.overview[0].e.uuid)) {
- /* this is just the summary box, not the tasks */
- document.getElementById("esr-" + jso.overview[0].e.uuid).innerHTML =
- sai_event_summary_render(jso.overview[0], now_ut, 1);
-
- /* if the task status icons exist, update their state */
-
- for (n = jso.overview[0].t.length - 1; n >= 0; n--)
- refresh_state(jso.overview[0].t[n]);
+ document.getElementById("ev-group-" + jso.overview[0].e.uuid)) {
+ /* completely replace the event HTML to capture new runs/tasks */
+ document.getElementById("ev-group-" + jso.overview[0].e.uuid).outerHTML =
+ sai_event_render(jso.overview[0], now_ut, 1);
+
+ /* restore progress bars via refresh_state */
+ if (jso.overview[0].t) {
+ for (n = jso.overview[0].t.length - 1; n >= 0; n--)
+ refresh_state(jso.overview[0].t[n]);
+ }
update_summary_and_progress(jso.overview[0].e.uuid);
@@ -2469,7 +2518,15 @@ function ws_open_sai()
/* update task summary if shown anywhere */
if (document.getElementById("taskinfo-" + jso.t.uuid)) {
- console.log("FOUND taskinfo-" + jso.t.uuid);
+ if (typeof window.current_task_run !== 'undefined' && window.current_task_run !== jso.t.run) {
+ if (document.getElementById("sai-task-logs"))
+ document.getElementById("sai-task-logs").innerHTML = "";
+ /* update the URL without reloading so sharing works */
+ const newUrl = new URL(window.location.href);
+ newUrl.searchParams.set('run', jso.t.run);
+ window.history.replaceState({}, '', newUrl);
+ }
+ window.current_task_run = jso.t.run;
document.getElementById("taskinfo-" + jso.t.uuid).innerHTML = sai_taskinfo_render(jso);
if (jso.e) {
if (document.getElementById("esr-" + jso.e.uuid))
@@ -2495,6 +2552,7 @@ function ws_open_sai()
if (url_task_uuid === jso.t.uuid &&
document.getElementById("sai_sticky")) {
+ window.current_task_run = jso.t.run;
document.getElementById("sai_sticky").innerHTML =
"<div class=\"taskinfo\" id=\"taskinfo-" +
san(jso.t.uuid) + "\">" +
@@ -2536,15 +2594,21 @@ function ws_open_sai()
console.log(rs);
sai.send(rs);
+ var tid = san(e.srcElement.id.substring(8));
+ if (new URLSearchParams(window.location.search).get('run')) {
+ window.location.search = '?task=' + tid;
+ return;
+ }
+
/*
* and immediately re-request the task info, so we can get
* the new logs
*/
- var tid = san(e.srcElement.id.substring(8));
var rq = "{\"schema\":" +
"\"com.warmcat.sai.taskinfo\"," +
"\"js_api_version\": " + SAI_JS_API_VERSION + "," +
"\"logs\": 1," +
+ "\"run\": -1," +
"\"last_log_ts\":" + last_log_timestamp + "," +
"\"task_hash\":" +
JSON.stringify(tid) + "}";
diff --git a/assets/tc-msvc-schannel.svg b/assets/tc-msvc-schannel.svg
new file mode 100644
index 0000000..dd47f12
--- /dev/null
+++ b/assets/tc-msvc-schannel.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3.816 3.816" height="14.423" width="14.423" xmlns:v="https://vecta.io/nano"><circle r="1.908" cy="1.908" cx="1.908" opacity=".12" fill="#f9f9f9"/><path d="M2.382 3.194c-.03-.01-.154-.14-.475-.495l-.448-.48c-.007 0-.14.104-.297.232-.33.27-.3.258-.483.183-.172-.07-.156-.002-.16-.713s-.01-.663.115-.724c.217-.105.192-.113.533.166l.29.23c.005 0 .208-.218.45-.485.456-.502.467-.512.553-.512.028 0 .61.275.64.303.056.05.055.024.055 1.01 0 1.113.032.967-.244 1.102-.407.198-.45.213-.53.183zm.117-1.3l-.01-.524a19.92 19.92 0 0 0-.634.52c-.014.017-.012.023.018.047l.322.262.296.23c.005-.003.01-.243.01-.534zm-1.4.1a.56.56 0 0 0 .075-.095 2.9 2.9 0 0 0-.298-.326c-.015 0-.026.556-.012.627l.008.04.076-.082.15-.166z" fill="#b88aed"/><path d="M2.312 3.12l-.395-.43-.387-.426-.07-.074-.268.22c-.37.305-.33.29-.502.214-.18-.078-.163.002-.162-.714s-.014-.64.142-.707c.188-.08.163-.088.407.11l.265.216.112.086c.006 0 .453-.485.792-.86.11-.12.14-.14.188-.13.058.015.063.046.063.394v.32l-.336.276-.34.288c-.004.013.003.02.407.347l.273.227c.015.022.01.635-.004.668-.012.026-.062.054-.095.054-.006 0-.046-.036-.09-.08zM1.036 2.084l.16-.186a3.42 3.42 0 0 0-.331-.359c-.015 0-.03.67-.015.706.01.028.026.014.186-.162zM2.485 3.19c.008-.003.02-.003.022.001-.03.006-.032.003-.022-.001zm.66-.39zm0-.035z" fill="#9260cc"/><path d="M.708 2.62c-.202-.083-.18.002-.18-.72l.012-.607c.012-.055.084-.012.186.1l.107.116.004.174.006.39c.001.26-.013.256.183.045.175-.19.175-.19.22-.136l.11.12c.04.044.075.084.075.09s-.214.188-.495.415c-.083.067-.1.068-.227.016zm1.043-.795c-.067-.058-.07-.064-.043-.078.053-.027.14-.025.175.003l.034.028-.046.048c-.057.058-.05.058-.12-.001zm.187-.087c-.012-.013-.01-.017.018-.024.062-.015.18-.114.195-.164.007-.024.078-.037.078-.015 0 .013-.042.034-.05.025a1.37 1.37 0 0 1-.207.184c-.01.006-.023.003-.032-.007zm-.315-.112c.013-.001.02.006.02.008 0 .01-.01.005-.02-.008zm-.122-.02c0-.006.005-.015.012-.02.012.01.006.02 0 .023zm.522-.001c.004-.006.012-.012.018-.012.01.018.003.023-.007.023zm-.432-.1c.012-.016.026-.03.032-.03s-.001.013-.017.03c-.035.037-.042.038-.015 0zm.666-.018c.004-.006.013-.012.02-.012 0 .018-.008.023-.014.023zm.055-.036c.003-.008.012-.018.02-.02.008.02-.002.028-.01.03zm-.636-.04zm.035-.035z" fill="#6e40a4"/></svg>
diff --git a/src/common/c-sqlite3.c b/src/common/c-sqlite3.c
index c37f028..0aeab79 100644
--- a/src/common/c-sqlite3.c
+++ b/src/common/c-sqlite3.c
@@ -75,7 +75,7 @@ sai_event_db_ensure_open(struct lws_context *cx, lws_dll2_owner_t *sqlite3_cache
return 3;
}
- sai_sqlite3_statement(*ppdb, "CREATE UNIQUE INDEX IF NOT EXISTS idx_task_uuid ON tasks(uuid);", "create task index");
+ sai_sqlite3_statement(*ppdb, "CREATE UNIQUE INDEX IF NOT EXISTS idx_task_uuid ON tasks(uuid, run);", "create task index");
sai_sqlite3_statement(*ppdb, "PRAGMA journal_mode=WAL;", "set WAL");
@@ -95,6 +95,11 @@ sai_event_db_ensure_open(struct lws_context *cx, lws_dll2_owner_t *sqlite3_cache
sai_sqlite3_statement(*ppdb, "CREATE INDEX IF NOT EXISTS idx_art_task ON artifacts(task_uuid);", "create artifact index");
+
+ /* Migrate the unique index to include the run column */
+ sqlite3_exec(*ppdb, "DROP INDEX IF EXISTS idx_task_uuid;", NULL, NULL, NULL);
+ sqlite3_exec(*ppdb, "CREATE UNIQUE INDEX idx_task_uuid ON tasks(uuid, run);", NULL, NULL, NULL);
+
sc = malloc(sizeof(*sc));
if (!sc) {
lwsl_err("%s: unable to alloc sc for %s\n", __func__, filepath);
diff --git a/src/common/include/private.h b/src/common/include/private.h
index b4773c8..cdf0ba9 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -221,6 +221,7 @@ typedef struct {
char told_ongoing;
char rebuildable;
+ int run;
} sai_task_t;
struct saib_logproxy {
@@ -374,6 +375,7 @@ typedef struct {
/* builder can report this along with step completion */
unsigned int avail_mem_kib;
unsigned int avail_sto_kib;
+ int run;
} sai_log_t;
typedef struct {
@@ -395,6 +397,7 @@ typedef struct {
int uid;
int fd;
char sent_json;
+ int run;
} sai_artifact_t;
/* communication part of resource allocation requests */
@@ -624,6 +627,7 @@ typedef struct sai_browse_rx_taskinfo {
unsigned int js_api_version;
unsigned int offset;
uint8_t logs;
+ int run;
} sai_browse_rx_taskinfo_t;
/* sai-power -> sai-server, tells it that a platform is being powered up */
@@ -794,9 +798,9 @@ extern const lws_struct_map_t
lsm_schema_map_ta[1],
lsm_schema_map_plat_simple[1],
lsm_event[12],
- lsm_task[30],
- lsm_log[7],
- lsm_artifact[8],
+ lsm_task[31],
+ lsm_log[8],
+ lsm_artifact[9],
lsm_plat_list[1],
lsm_schema_map_plat[1],
lsm_task_rej[4],
diff --git a/src/common/struct-metadata.c b/src/common/struct-metadata.c
index a07826d..242ead9 100644
--- a/src/common/struct-metadata.c
+++ b/src/common/struct-metadata.c
@@ -178,6 +178,7 @@ const lws_struct_map_t lsm_task[] = {
LSM_UNSIGNED (sai_task_t, est_compute_ms, "est_compute_ms"),
LSM_SIGNED (sai_task_t, parallel, "parallel"),
LSM_SIGNED (sai_task_t, rebuildable, "rebuildable"),
+ LSM_SIGNED (sai_task_t, run, "run"),
};
const lws_struct_map_t lsm_schema_json_map_task[] = {
@@ -242,6 +243,7 @@ const lws_struct_map_t lsm_log[] = {
LSM_UNSIGNED (sai_log_t, finished, "finished"),
LSM_CARRAY (sai_log_t, task_uuid, "task_uuid"),
LSM_STRING_PTR (sai_log_t, log, "log"),
+ LSM_SIGNED (sai_log_t, run, "run"),
};
const lws_struct_map_t lsm_schema_json_map_log[] = {
@@ -288,6 +290,7 @@ const lws_struct_map_t lsm_artifact[] = {
LSM_BLOB_PTR (sai_artifact_t, blob, "blob"),
LSM_UNSIGNED (sai_artifact_t, timestamp, "timestamp"),
LSM_UNSIGNED (sai_artifact_t, len, "len"),
+ LSM_SIGNED (sai_artifact_t, run, "run"),
};
const lws_struct_map_t lsm_schema_json_map_artifact[] = {
diff --git a/src/power/p-ws-server.c b/src/power/p-ws-server.c
index 02403b1..1e88492 100644
--- a/src/power/p-ws-server.c
+++ b/src/power/p-ws-server.c
@@ -202,8 +202,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
lws_struct_args_t a;
struct lejp_ctx ctx;
- lwsl_notice("%s: PPPPPPPP len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps);
- lwsl_hexdump_notice(buf, len);
+ // lwsl_notice("%s: PPPPPPPP len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps);
+ // lwsl_hexdump_notice(buf, len);
memset(&a, 0, sizeof(a));
a.map_st[0] = lsm_saip_rx_map;
diff --git a/src/server/s-central.c b/src/server/s-central.c
index 82fd9ab..2a08d86 100644
--- a/src/server/s-central.c
+++ b/src/server/s-central.c
@@ -119,7 +119,8 @@ sais_central_clean_abandoned(struct vhd *vhd)
lws_snprintf(s, sizeof(s),
"SELECT uuid FROM tasks WHERE "
"(state = %d OR state = %d) AND "
- "started != 0 AND started < %llu",
+ "started != 0 AND started < %llu AND "
+ "run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)",
SAIES_PASSED_TO_BUILDER,
SAIES_BEING_BUILT, (unsigned long long)
(lws_now_secs() -
@@ -226,7 +227,8 @@ sais_central_gc_deleted_events_cb(lws_sorted_usec_list_t *sul)
/* Pick 1 task that is stopped to garbage collect */
/* 0=WAITING, 3=SUCCESS, 4=FAIL, 5=CANCELLED, 8=NOT_READY, 9=STEP_SUCCESS, 10=PAUSED */
- lws_snprintf(q, sizeof(q), "SELECT uuid FROM tasks WHERE state IN (0, 3, 4, 5, 8, 9, 10) LIMIT 1");
+ lws_snprintf(q, sizeof(q), "SELECT uuid FROM tasks WHERE state IN (0, 3, 4, 5, 8, 9, 10) "
+ "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid) LIMIT 1");
if (sqlite3_prepare_v2(pdb, q, -1, &tsm, NULL) == SQLITE_OK) {
if (sqlite3_step(tsm) == SQLITE_ROW) {
diff --git a/src/server/s-task-helpers.c b/src/server/s-task-helpers.c
index b6c7d2b..2fa22aa 100644
--- a/src/server/s-task-helpers.c
+++ b/src/server/s-task-helpers.c
@@ -76,7 +76,7 @@ int
sais_bind_task_to_builder(struct vhd *vhd, const char *builder_name,
const char *builder_uuid, const char *task_uuid)
{
- char update[384], esc[96], esc1[96], esc2[96], event_uuid[33];
+ char update[512], esc[96], esc1[96], esc2[96], event_uuid[33];
struct lwsac *ac = NULL;
sai_event_t *e = NULL;
lws_dll2_owner_t o;
@@ -132,8 +132,8 @@ sais_bind_task_to_builder(struct vhd *vhd, const char *builder_name,
*/
lws_snprintf(update, sizeof(update),
- "update tasks set builder='%s',builder_name='%s' where uuid='%s'",
- esc1, esc, esc2);
+ "update tasks set builder='%s',builder_name='%s' where uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ esc1, esc, esc2, esc2);
if (sqlite3_exec((sqlite3 *)e->pdb, update, NULL, NULL, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
@@ -155,7 +155,7 @@ int
sais_set_task_state(struct vhd *vhd, const char *task_uuid,
sai_event_state_t state, uint64_t started, uint64_t duration)
{
- char update[384], esc1[96], esc2[96], esc3[32], esc4[32], event_uuid[33];
+ char update[512], esc1[96], esc2[96], esc3[32], esc4[32], event_uuid[33];
sai_event_state_t oes, sta, task_ostate, ostate = state;
unsigned int count = 0, count_good = 0, count_bad = 0;
uint64_t started_orig = started;
@@ -207,7 +207,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid,
* grab the current state of it for seeing if it changed
*/
lws_snprintf(update, sizeof(update),
- "select state from tasks where uuid='%s'", esc2);
+ "select state from tasks where uuid='%s' order by run desc limit 1", esc2);
if (sqlite3_exec((sqlite3 *)e->pdb, update,
sql3_get_integer_cb, &task_ostate, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
@@ -240,9 +240,9 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid,
*/
lws_snprintf(update, sizeof(update),
- "update tasks set state=%d%s%s%s where uuid='%s'", state,
+ "update tasks set state=%d%s%s%s where uuid='%s' and run=(select max(run) from tasks where uuid='%s')", state,
esc3, esc4, state == SAIES_WAITING && started_orig == 1 ?
- ",build_step=0" : "", esc2);
+ ",build_step=0" : "", esc2, esc2);
if (sqlite3_exec((sqlite3 *)e->pdb, update, NULL, NULL, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
@@ -280,7 +280,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid,
* So, how many tasks for this event?
*/
- if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks",
+ if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) from tasks group by uuid)",
sql3_get_integer_cb, &count, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
sqlite3_errmsg(vhd->server.pdb));
@@ -291,7 +291,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid,
* ... how many completed well?
*/
- if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks where state == 3",
+ if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) as mx, state from tasks group by uuid) where state == 3",
sql3_get_integer_cb, &count_good, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
sqlite3_errmsg(vhd->server.pdb));
@@ -302,7 +302,7 @@ sais_set_task_state(struct vhd *vhd, const char *task_uuid,
* ... how many failed?
*/
- if (sqlite3_exec((sqlite3 *)e->pdb, "select count(state) from tasks where state == 4",
+ if (sqlite3_exec((sqlite3 *)e->pdb, "select count(*) from (select max(run) as mx, state from tasks group by uuid) where state == 4",
sql3_get_integer_cb, &count_bad, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, update,
sqlite3_errmsg(vhd->server.pdb));
@@ -375,7 +375,7 @@ bail:
int
sais_task_pause(struct vhd *vhd, const char *task_uuid)
{
- char event_uuid[33], esc_uuid[129], q[128];
+ char event_uuid[33], esc_uuid[129], q[384];
int build_step = -1, state = -1;
sqlite3 *pdb = NULL;
@@ -388,7 +388,7 @@ sais_task_pause(struct vhd *vhd, const char *task_uuid)
lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid));
lws_snprintf(q, sizeof(q),
- "select build_step,state from tasks where uuid='%s'",
+ "select build_step,state from tasks where uuid='%s' order by run desc limit 1",
esc_uuid);
if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &build_step,
@@ -408,8 +408,8 @@ sais_task_pause(struct vhd *vhd, const char *task_uuid)
if (build_step > 0) {
build_step--;
lws_snprintf(q, sizeof(q),
- "update tasks set build_step=%d where uuid='%s'",
- build_step, esc_uuid);
+ "update tasks set build_step=%d where uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ build_step, esc_uuid, esc_uuid);
sqlite3_exec(pdb, q, NULL, NULL, NULL);
}
sais_task_stop_on_builders(vhd, task_uuid);
@@ -486,7 +486,7 @@ sais_task_stop_on_builders(struct vhd *vhd, const char *task_uuid)
builder_name[0] = '\0';
lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid));
- lws_snprintf(q, sizeof(q), "select builder_name from tasks where uuid='%s'",
+ lws_snprintf(q, sizeof(q), "select builder_name from tasks where uuid='%s' order by run desc limit 1",
esc_uuid);
if (sqlite3_exec(pdb, q, sql3_get_string_cb, builder_name, NULL) !=
SQLITE_OK ||
@@ -565,36 +565,51 @@ sais_task_clear_build_and_logs(struct vhd *vhd, const char *task_uuid, int from_
return SAI_DB_RESULT_ERROR;
}
- lws_sql_purify(esc, task_uuid, sizeof(esc));
- lws_snprintf(cmd, sizeof(cmd), "delete from logs where task_uuid='%s'",
- esc);
-
- ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL);
- if (ret != SQLITE_OK) {
- sai_event_db_close(&vhd->sqlite3_cache, &pdb);
- if (ret == SQLITE_BUSY)
- return SAI_DB_RESULT_BUSY;
- lwsl_err("%s: %s: %s: fail\n", __func__, cmd,
- sqlite3_errmsg(pdb));
- return SAI_DB_RESULT_ERROR;
- }
- lws_snprintf(cmd, sizeof(cmd), "delete from artifacts where task_uuid='%s'",
- esc);
+ struct lwsac *ac = NULL;
+ lws_dll2_owner_t o;
- ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL);
- if (ret != SQLITE_OK) {
- sai_event_db_close(&vhd->sqlite3_cache, &pdb);
- if (ret == SQLITE_BUSY)
- return SAI_DB_RESULT_BUSY;
- lwsl_err("%s: %s: %s: fail\n", __func__, cmd,
- sqlite3_errmsg(pdb));
- return SAI_DB_RESULT_ERROR;
+ lws_sql_purify(esc, task_uuid, sizeof(esc));
+ lws_snprintf(cmd, sizeof(cmd), " and uuid='%s'", esc);
+
+ ret = lws_struct_sq3_deserialize(pdb, cmd, "run desc", lsm_schema_sq3_map_task, &o, &ac, 0, 1);
+ if (ret >= 0 && o.head) {
+ sai_task_t *t = lws_container_of(o.head, sai_task_t, list);
+
+ if (!from_rejection)
+ t->run++;
+
+ t->started = 0;
+ t->duration = 0;
+ t->build_step = 0;
+
+ if (!from_rejection) {
+ /* Reset builder bindings for the new run so it can be picked up by any suitable builder */
+ t->builder[0] = '\0';
+ t->builder_name[0] = '\0';
+ t->server_name = "";
+ /* serialize as a new row */
+ lws_struct_sq3_serialize(pdb, lsm_schema_sq3_map_task, &o, 0);
+ } else {
+ t->state = SAIES_WAITING;
+ /* update the existing row if it's a rejection */
+ lws_snprintf(cmd, sizeof(cmd),
+ "update tasks set state=%d,started=0,duration=0,build_step=0 where uuid='%s' and run=%d",
+ SAIES_WAITING, esc, t->run);
+ sqlite3_exec(pdb, cmd, NULL, NULL, NULL);
+ }
}
-
+
+ lwsac_free(&ac);
sai_event_db_close(&vhd->sqlite3_cache, &pdb);
- /* 1,1 == reset started and duration in db for task to 0 */
- sais_set_task_state(vhd, task_uuid, SAIES_WAITING, 1, 1);
+ if (!from_rejection) {
+ sais_set_task_state(vhd, task_uuid, SAIES_WAITING, 0, 0);
+ sais_taskchange(vhd->h_ss_websrv, task_uuid, SAIES_WAITING);
+ sais_eventchange(vhd->h_ss_websrv, event_uuid, SAIES_WAITING);
+ } else {
+ sais_taskchange(vhd->h_ss_websrv, task_uuid, SAIES_WAITING);
+ sais_eventchange(vhd->h_ss_websrv, event_uuid, SAIES_WAITING);
+ }
sais_task_stop_on_builders(vhd, task_uuid);
@@ -623,7 +638,7 @@ sais_task_clear_build_and_logs(struct vhd *vhd, const char *task_uuid, int from_
sai_db_result_t
sais_task_rebuild_last_step(struct vhd *vhd, const char *task_uuid)
{
- char esc[96], cmd[256], event_uuid[33];
+ char esc[96], cmd[384], event_uuid[33];
struct lwsac *ac = NULL;
sqlite3 *pdb = NULL;
lws_dll2_owner_t o;
@@ -660,8 +675,8 @@ sais_task_rebuild_last_step(struct vhd *vhd, const char *task_uuid)
if (task->build_step > 0) {
lws_snprintf(cmd, sizeof(cmd),
- "update tasks set build_step=%d where uuid='%s'",
- task->build_step - 1, esc);
+ "update tasks set build_step=%d where uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ task->build_step - 1, esc, esc);
ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
diff --git a/src/server/s-task.c b/src/server/s-task.c
index c2a2668..9bb4ca7 100644
--- a/src/server/s-task.c
+++ b/src/server/s-task.c
@@ -165,6 +165,29 @@ sais_prune_inflight_list(struct vhd *vhd)
/*
+ * Helper to identify and fix stale corrupted legacy tasks
+ */
+static int
+sais_check_and_fix_stale_task(sqlite3 *pdb, sai_task_t *t)
+{
+ int max_run = -1;
+ char q[200], esc[100];
+
+ lws_sql_purify(esc, t->uuid, sizeof(esc));
+ lws_snprintf(q, sizeof(q), "select max(run) from tasks where uuid='%s'", esc);
+
+ if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &max_run, NULL) == SQLITE_OK) {
+ if (max_run >= 0 && (int)t->run < max_run) {
+ lwsl_err("%s: task %s run %d is stale (max %d), marking deleted\n", __func__, esc, t->run, max_run);
+ lws_snprintf(q, sizeof(q), "update tasks set state=7 where uuid='%s' and run=%d", esc, t->run);
+ sqlite3_exec(pdb, q, NULL, NULL, NULL);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
* Find the most recent task that still needs doing for platform, on any event
*/
static const sai_task_t *
@@ -234,8 +257,8 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb,
*/
lws_snprintf(query, sizeof(query), "select count(state) from tasks where "
- "(state = 0 or state = 9) and platform = '%s' and "
- "(builder_name IS NULL or builder_name = '' or builder_name = '%s')",
+ "state IN(0,9) and platform='%s' and "
+ "(builder_name IS NULL or builder_name IN('','%s'))",
esc_plat, esc_bname);
m = sqlite3_exec(pdb, query, sql3_get_integer_cb, &pending_count, NULL);
@@ -367,9 +390,8 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb,
lws_sql_purify(esc_taskname, fti->taskname, sizeof(esc_taskname));
lws_snprintf(pf, sizeof(pf),
- " and (state == 0 or state == 9) and "
- "(platform == '%s') and (taskname == '%s') and "
- "(builder_name IS NULL or builder_name == '' or builder_name == '%s')",
+ " and state IN(0,9) and platform='%s' and taskname='%s' and "
+ "(builder_name IS NULL or builder_name IN('','%s'))",
esc_plat, esc_taskname, esc_bname);
lwsac_free(&pss->ac_alloc_task);
@@ -380,6 +402,14 @@ sais_task_pending(struct vhd *vhd, struct pss *pss, sai_plat_t *cb,
if (n < 0 || !owner.count)
goto next1;
+ sai_task_t *t = lws_container_of(owner.head, sai_task_t, list);
+ if (sais_check_and_fix_stale_task(pdb, t)) {
+ sai_event_db_close(&vhd->sqlite3_cache, &pdb);
+ lwsac_free(&ac);
+ lwsac_free(&failed_ac);
+ return NULL;
+ }
+
lwsl_notice("%s: Prioritizing failed task for %s ('%s')\n",
__func__, platform, fti->taskname);
@@ -399,8 +429,8 @@ next1: ;
/* We have fallen back to doing tasks earliest-first */
lws_snprintf(pf, sizeof(pf),
- " and (state = 0 or state = 9) and (platform = '%s') and "
- "(builder_name IS NULL or builder_name = '' or builder_name = '%s')",
+ " and state IN(0,9) and platform='%s' and "
+ "(builder_name IS NULL or builder_name IN('','%s'))",
esc_plat, esc_bname);
lwsac_free(&pss->ac_alloc_task);
@@ -413,6 +443,14 @@ next1: ;
if (n < 0 || !owner.count || !pss->ac_alloc_task)
goto close_next;
+ sai_task_t *t = lws_container_of(owner.head, sai_task_t, list);
+ if (sais_check_and_fix_stale_task(pdb, t)) {
+ sai_event_db_close(&vhd->sqlite3_cache, &pdb);
+ lwsac_free(&ac);
+ lwsac_free(&failed_ac);
+ return NULL;
+ }
+
lwsl_info("%s: orig exit\n", __func__);
sai_event_db_close(&vhd->sqlite3_cache, &pdb);
lwsac_free(&ac);
@@ -815,7 +853,7 @@ sais_create_and_offer_task_step(struct vhd *vhd, const char *task_uuid)
lws_sql_purify(esc_uuid, task_uuid, sizeof(esc_uuid));
lws_snprintf(update, sizeof(update), " and state != 4 and uuid='%s'", esc_uuid);
- n = lws_struct_sq3_deserialize(pdb, update, NULL,
+ n = lws_struct_sq3_deserialize(pdb, update, "run desc",
lsm_schema_sq3_map_task, &o, &ac, 0, 1);
if (n < 0 || !o.head) {
lwsl_warn("%s: bailing as nothing with state != 4\n", __func__);
diff --git a/src/server/s-webops.c b/src/server/s-webops.c
index bd665aa..22f68ec 100644
--- a/src/server/s-webops.c
+++ b/src/server/s-webops.c
@@ -227,7 +227,7 @@ sais_event_reset(struct vhd *vhd, const char *event_uuid)
vhd->sqlite3_path_lhs, event_uuid, 0, &pdb))
return SAI_DB_RESULT_ERROR;
- if (lws_struct_sq3_deserialize(pdb, NULL, NULL,
+ if (lws_struct_sq3_deserialize(pdb, " and run=(select max(run) from tasks t2 where t2.uuid=tasks.uuid)", NULL,
lsm_schema_sq3_map_task,
&o, &ac, 0, 999) >= 0) {
@@ -317,7 +317,8 @@ sais_event_delete(struct vhd *vhd, const char *event_uuid)
/* 3. Drop active builders gracefully without loading huge JSON objects */
if (sai_event_db_ensure_open(vhd->context, &vhd->sqlite3_cache,
vhd->sqlite3_path_lhs, event_uuid, 0, &pdb) == 0) {
- lws_snprintf(qu, sizeof(qu), "SELECT uuid FROM tasks WHERE state != 0 AND state != 3 AND state != 4 AND state != 5");
+ lws_snprintf(qu, sizeof(qu), "SELECT uuid FROM tasks WHERE state != 0 AND state != 3 AND state != 4 AND state != 5 "
+ "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)");
if (sqlite3_prepare_v2(pdb, qu, -1, &sm, NULL) == SQLITE_OK) {
while (sqlite3_step(sm) == SQLITE_ROW) {
const unsigned char *task_uuid = sqlite3_column_text(sm, 0);
@@ -360,7 +361,7 @@ sais_plat_reset(struct vhd *vhd, const char *event_uuid, const char *platform)
return SAI_DB_RESULT_ERROR;
lws_sql_purify(esc, platform, sizeof(esc));
- lws_snprintf(filt, sizeof(filt), " and platform='%s'", esc);
+ lws_snprintf(filt, sizeof(filt), " and platform='%s' and run=(select max(run) from tasks t2 where t2.uuid=tasks.uuid)", esc);
if (lws_struct_sq3_deserialize(pdb, filt, NULL,
lsm_schema_sq3_map_task,
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 413f15e..6131260 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -116,6 +116,17 @@ sais_dump_logs_to_db(lws_sorted_usec_list_t *sul)
* more efficient
*/
+ int run = 0;
+ char q[128];
+
+ lws_snprintf(q, sizeof(q), "select max(run) from tasks where uuid='%s'", lcpt->uuid);
+ sqlite3_exec(pdb, q, sql3_get_integer_cb, &run, NULL);
+
+ lws_start_foreach_dll(struct lws_dll2 *, pq, lcpt->cache.head) {
+ sai_log_t *hl = lws_container_of(pq, sai_log_t, list);
+ hl->run = run;
+ } lws_end_foreach_dll(pq);
+
sqlite3_exec(pdb, "BEGIN TRANSACTION", NULL, NULL, &err);
if (err)
sqlite3_free(err);
@@ -251,8 +262,8 @@ sais_log_to_db(struct vhd *vhd, sai_log_t *log)
lws_sql_purify(esc_uuid, log->task_uuid, sizeof(esc_uuid));
lws_snprintf(q, sizeof(q),
- "UPDATE tasks SET build_step=%d WHERE uuid='%s'",
- step, esc_uuid);
+ "UPDATE tasks SET build_step=%d WHERE uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ step, esc_uuid, esc_uuid);
if (sai_sqlite3_statement(pdb, q, "update build_step"))
lwsl_err("%s: failed to update build_step\n", __func__);
@@ -455,8 +466,8 @@ sais_builder_disconnected(struct vhd *vhd, struct lws *wsi)
lws_snprintf(q, sizeof(q),
"SELECT uuid FROM tasks WHERE "
- "(state = %d OR state = %d) AND "
- "builder_name = ?",
+ "builder_name=? AND (state = %d OR state = %d) "
+ "AND run=(SELECT max(run) FROM tasks t2 WHERE t2.uuid = tasks.uuid)",
SAIES_PASSED_TO_BUILDER,
SAIES_BEING_BUILT);
@@ -532,7 +543,7 @@ static int
sais_process_rej(struct vhd *vhd, struct pss *pss,
sai_plat_t *sp, sai_rejection_t *rej)
{
- char event_uuid[33], do_remove_uuid = 0, q[128], esc_uuid[129];
+ char event_uuid[33], do_remove_uuid = 0, q[384], esc_uuid[129];
int n, build_step = -1;
sqlite3 *pdb = NULL;
sai_uuid_list_t *ul;
@@ -553,7 +564,7 @@ sais_process_rej(struct vhd *vhd, struct pss *pss,
lws_sql_purify(esc_uuid, rej->task_uuid, sizeof(esc_uuid));
lws_snprintf(q, sizeof(q),
- "select build_step from tasks where uuid='%s'",
+ "select build_step from tasks where uuid='%s' order by run desc limit 1",
esc_uuid);
if (sqlite3_exec(pdb, q, sql3_get_integer_cb, &build_step,
@@ -567,22 +578,23 @@ sais_process_rej(struct vhd *vhd, struct pss *pss,
build_step++;
lws_snprintf(q, sizeof(q),
"update tasks set build_step=%d "
- "where state != 4 and uuid='%s'",
- build_step, esc_uuid);
- sqlite3_exec(pdb, q, NULL, NULL, NULL);
+ "where uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ build_step, esc_uuid, esc_uuid);
+ if (sai_sqlite3_statement(pdb, q, "update build_step accepted"))
+ lwsl_err("%s: failed to update build_step\n", __func__);
lwsl_notice("%s: &&&&&&&& build_step set to %d\n", __func__, build_step);
if (build_step == 1) {
pss->first_log_timestamp = (uint64_t)lws_now_secs();
lws_snprintf(q, sizeof(q),
- "update tasks set started=%llu where uuid='%s'",
- (unsigned long long)pss->first_log_timestamp, esc_uuid);
+ "update tasks set started=%llu where uuid='%s' and run=(select max(run) from tasks where uuid='%s')",
+ (unsigned long long)pss->first_log_timestamp, esc_uuid, esc_uuid);
lwsl_warn("%s: &&&&&&&&&&&&&&&&&&&&&&&&&& setting task %s started to %llu\n",
__func__, esc_uuid, (unsigned long long)pss->first_log_timestamp);
- if (sqlite3_exec(pdb, q, NULL, NULL, NULL) != SQLITE_OK)
+ if (sai_sqlite3_statement(pdb, q, "update started"))
lwsl_notice("%s: unable to set started\n", __func__);
}
@@ -1074,7 +1086,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b
lws_sql_purify(esc, ap->task_uuid, sizeof(esc));
lws_snprintf(s, sizeof(s)," and uuid == \"%s\"", esc);
n = lws_struct_sq3_deserialize(pss->pdb_artifact, s,
- NULL, lsm_schema_sq3_map_task,
+ "run desc", lsm_schema_sq3_map_task,
&o, &ac, 0, 1);
if (n < 0 || !o.head) {
sai_event_db_close(&vhd->sqlite3_cache, &pss->pdb_artifact);
@@ -1108,6 +1120,8 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b
sai_uuid16_create(pss->vhd->context,
ap->artifact_down_nonce);
+ ap->run = task->run;
+
lws_dll2_owner_clear(&o);
lws_dll2_add_head(&ap->list, &o);
@@ -1210,7 +1224,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b
ap = (sai_artifact_t *)pss->a.dest;
lws_sql_purify(esc, ap->task_uuid, sizeof(esc));
- lws_snprintf(s, sizeof(s)," select state from tasks where uuid == \"%s\"", esc);
+ lws_snprintf(s, sizeof(s)," select state from tasks where uuid == \"%s\" order by run desc limit 1", esc);
if (sqlite3_exec((sqlite3 *)pss->pdb_artifact, s,
sql3_get_integer_cb, &state, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: %s: fail\n", __func__, s,
diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c
index c8d4f6e..b665f1d 100644
--- a/src/server/s-ws-web.c
+++ b/src/server/s-ws-web.c
@@ -76,6 +76,7 @@ static lws_struct_map_t lsm_browser_taskinfo[] = {
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, logs, "logs"),
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"),
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, last_log_ts, "last_log_ts"),
+ LSM_SIGNED (sai_browse_rx_taskinfo_t, run, "run"),
};
static const lws_struct_map_t lsm_schema_json_map[] = {
diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt
index 8ee5585..8eb8e40 100644
--- a/src/web/CMakeLists.txt
+++ b/src/web/CMakeLists.txt
@@ -142,6 +142,7 @@ add_dependencies(${SUB} sai_git_hash)
../../assets/tc-mingw32.svg
../../assets/tc-mingw64.svg
../../assets/tc-msvc.svg
+ ../../assets/tc-msvc-schannel.svg
../../assets/tc-wmbedtlsmsvc.svg
../../assets/tc-noptmsvc.svg
../../assets/ubuntu.svg
diff --git a/src/web/w-private.h b/src/web/w-private.h
index 8e4d74c..b2e0ae7 100644
--- a/src/web/w-private.h
+++ b/src/web/w-private.h
@@ -73,6 +73,7 @@ struct pss {
uint64_t sub_timestamp;
char sub_task_uuid[65];
+ int sub_run;
char specific_ref[65];
char specific_task[65];
char specific_project[96];
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index 01188f5..bc3bff2 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -61,6 +61,7 @@ static lws_struct_map_t lsm_browser_taskinfo[] = {
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"),
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, offset, "offset"),
LSM_UNSIGNED (sai_browse_rx_taskinfo_t, last_log_ts, "last_log_ts"),
+ LSM_SIGNED (sai_browse_rx_taskinfo_t, run, "run"),
};
/*
@@ -120,6 +121,7 @@ enum {
typedef struct sai_browse_taskreply {
const sai_event_t *event;
const sai_task_t *task;
+ lws_dll2_owner_t runs;
} sai_browse_taskreply_t;
static lws_struct_map_t lsm_taskreply[] = {
@@ -127,6 +129,8 @@ static lws_struct_map_t lsm_taskreply[] = {
lsm_event, "e"),
LSM_CHILD_PTR (sai_browse_taskreply_t, task, sai_task_t, NULL,
lsm_task, "t"),
+ LSM_LIST (sai_browse_taskreply_t, runs, sai_task_t, list, NULL,
+ lsm_task, "runs"),
};
const lws_struct_map_t lsm_schema_json_map_taskreply[] = {
@@ -257,13 +261,13 @@ bail:
/* we leave an allocation in sch->query_ac ... */
static int
-saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
+saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub, int run_idx)
{
char qu[192], event_uuid[33], esc2[96], buf[4096 + LWS_PRE],
*start = buf + LWS_PRE, *p = start, *end = buf + sizeof(buf);
const sai_event_t *one_event = NULL;
sai_browse_taskreply_t task_reply;
- struct lwsac *query_ac = NULL;
+ struct lwsac *query_ac = NULL, *runs_ac = NULL, *art_ac = NULL;
sai_task_t *one_task = NULL;
lws_struct_serialize_t *js;
char esc[256], filt[128];
@@ -310,9 +314,19 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
*/
lws_sql_purify(esc, task_uuid, sizeof(esc));
- lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc);
- n = lws_struct_sq3_deserialize(pdb, qu, NULL, lsm_schema_sq3_map_task,
+ if (run_idx >= 0)
+ lws_snprintf(qu, sizeof(qu), " and uuid='%s' and run=%d", esc, run_idx);
+ else
+ lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc);
+ n = lws_struct_sq3_deserialize(pdb, qu, run_idx >= 0 ? NULL : "run desc", lsm_schema_sq3_map_task,
&o, &query_ac, 0, 1);
+
+ memset(&task_reply, 0, sizeof(task_reply));
+ lws_dll2_owner_clear(&task_reply.runs);
+ lws_snprintf(qu, sizeof(qu), " and uuid='%s'", esc);
+ lws_struct_sq3_deserialize(pdb, qu, "run desc", lsm_schema_sq3_map_task,
+ &task_reply.runs, &runs_ac, 0, 100);
+
sai_event_db_close(&pss->vhd->sqlite3_cache, &pdb);
if (n < 0 || !o.head)
goto bail;
@@ -364,8 +378,6 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
else
one_event = lws_container_of(o.head, sai_event_t, list);
- memset(&task_reply, 0, sizeof(task_reply));
-
/*
* We're sending a browser the specific task info that he
* asked for.
@@ -392,9 +404,15 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
do {
n = (int)lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p), &w);
+ if (n == LSJS_RESULT_ERROR) {
+ lws_struct_json_serialize_destroy(&js);
+ lwsl_notice("%s: taskinfo: error generating json\n", __func__);
+ goto bail;
+ }
+ p += w;
if (lws_ptr_diff_size_t(end, (uint8_t *)p) < 512) {
- saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(pss->vhd, start,
+ saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
lws_ptr_diff_size_t(p, start),
lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0));
p = start;
@@ -417,23 +435,23 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
pss->vhd->sqlite3_path_lhs, event_uuid,
0, &pdb)) {
- lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s')",
- one_task->uuid);
+ if (run_idx >= 0)
+ lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d",
+ one_task->uuid, run_idx);
+ else
+ lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d",
+ one_task->uuid, one_task->run);
if (lws_struct_sq3_deserialize(pdb, filt, NULL,
lsm_schema_sq3_map_artifact,
&owner,
- &query_ac, 0, 10))
+ &art_ac, 0, 10))
lwsl_err("%s: get afcts failed\n", __func__);
sai_event_db_close(&pss->vhd->sqlite3_cache, &pdb);
}
- if (n == LSJS_RESULT_ERROR) {
- lwsl_notice("%s: taskinfo: error generating json\n", __func__);
- goto bail;
- }
- p += w;
+
saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
lws_ptr_diff_size_t(p, start),
@@ -442,9 +460,18 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
/* does he want to subscribe to logs? */
if (logsub && !pss->subs_list.owner) {
strcpy(pss->sub_task_uuid, one_task->uuid);
+ pss->sub_run = run_idx >= 0 ? run_idx : one_task->run;
lws_dll2_add_head(&pss->subs_list, &pss->vhd->subs_owner);
pss->sub_timestamp = pss->initial_log_timestamp; /* where we got up to */
saiw_broadcast_logs_batch(pss->vhd, pss);
+ } else if (!strcmp(pss->sub_task_uuid, one_task->uuid)) {
+ /* If already subscribed to this task, track new runs automatically */
+ int new_run = run_idx >= 0 ? run_idx : one_task->run;
+ if (pss->sub_run != new_run) {
+ pss->sub_run = new_run;
+ pss->sub_timestamp = 0;
+ saiw_broadcast_logs_batch(pss->vhd, pss);
+ }
}
saiw_browser_broadcast_queue_builders(pss->vhd, pss);
@@ -492,11 +519,15 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub)
}
lwsac_free(&query_ac);
+ lwsac_free(&runs_ac);
+ lwsac_free(&art_ac);
return 0;
bail:
lwsac_free(&query_ac);
+ lwsac_free(&runs_ac);
+ lwsac_free(&art_ac);
return 1;
}
@@ -514,7 +545,7 @@ saiw_subs_task_state_change(struct vhd *vhd, const char *task_uuid)
struct pss *pss = lws_container_of(p, struct pss, subs_list);
if (!strcmp(pss->sub_task_uuid, task_uuid))
- saiw_pss_schedule_taskinfo(pss, task_uuid, 0);
+ saiw_pss_schedule_taskinfo(pss, task_uuid, 0, pss->sub_run);
} lws_end_foreach_dll(p);
@@ -529,7 +560,7 @@ saiw_browsers_task_state_change(struct vhd *vhd, const char *task_uuid)
struct pss *pss = lws_container_of(p, struct pss, same);
if (!pss->is_gitohashi)
- saiw_pss_schedule_taskinfo(pss, task_uuid, 0);
+ saiw_pss_schedule_taskinfo(pss, task_uuid, 0, -1);
} lws_end_foreach_dll(p);
return 0;
@@ -636,7 +667,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
else
pss->initial_log_timestamp = 0;
- if (saiw_pss_schedule_taskinfo(pss, ti->task_hash, !!ti->logs))
+ if (saiw_pss_schedule_taskinfo(pss, ti->task_hash, !!ti->logs, ti->run))
goto soft_error;
goto ok;
@@ -797,8 +828,8 @@ saiw_broadcast_logs_batch(struct vhd *vhd, struct pss *pss)
lwsac_free(&pss->logs_ac);
lws_snprintf(esc, sizeof(esc),
- "and task_uuid='%s' and timestamp > %llu",
- pss->sub_task_uuid,
+ "and task_uuid='%s' and run=%d and timestamp > %llu",
+ pss->sub_task_uuid, pss->sub_run,
(unsigned long long)pss->sub_timestamp);
// lwsl_notice("%s: collecting logs %s\n", __func__, esc);
@@ -1073,7 +1104,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss)
} else {
task_ac = NULL;
lws_dll2_owner_clear(&task_owner);
- if (lws_struct_sq3_deserialize(pdb, NULL, NULL,
+ if (lws_struct_sq3_deserialize(pdb, NULL, "taskname, platform",
lsm_schema_sq3_map_task, &task_owner,
&task_ac, 0, 999)) {
lwsl_err("%s: OVERVIEW 1 failed\n", __func__);