diff --git a/assets/sai.css b/assets/sai.css
index c2f5b1d..91f3c4c 100644
--- a/assets/sai.css
+++ b/assets/sai.css
@@ -951,3 +951,26 @@ img.branch {
.ansi-bg-magenta { background-color: #A0A; }
.ansi-bg-cyan { background-color: #0AA; }
.ansi-bg-white { background-color: #AAA; }
+
+.step-progress {
+ display: flex;
+ width: 16px; /* Match the icon width */
+ height: 2px;
+ margin-top: 1px;
+}
+
+.step-bar {
+ flex-grow: 1;
+ height: 100%;
+ background-color: white;
+ margin-right: 1px;
+ border: solid 1px #a0a0a0;
+}
+
+.step-bar:last-child {
+ margin-right: 0;
+}
+
+.step-bar-completed {
+ background-color: black;
+}
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 9439a90..7997ad2 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -129,6 +129,8 @@ typedef struct {
uint64_t duration;
int state;
int uid;
+ int build_step;
+ int build_step_count;
char told_ongoing;
} sai_task_t;
@@ -492,7 +494,7 @@ extern const lws_struct_map_t
lsm_schema_map_ta[1],
lsm_schema_map_plat_simple[1],
lsm_event[9],
- lsm_task[21],
+ lsm_task[23],
lsm_log[7],
lsm_artifact[8],
lsm_plat_list[1],
diff --git a/src/common/struct-metadata.c b/src/common/struct-metadata.c
index d13f3ec..58c8dc5 100644
--- a/src/common/struct-metadata.c
+++ b/src/common/struct-metadata.c
@@ -136,6 +136,8 @@ const lws_struct_map_t lsm_task[] = {
LSM_STRING_PTR (sai_task_t, git_ref, "git_ref"),
LSM_STRING_PTR (sai_task_t, git_hash, "git_hash"),
LSM_STRING_PTR (sai_task_t, git_repo_url, "git_repo_url"),
+ LSM_SIGNED (sai_task_t, build_step, "build_step"),
+ LSM_SIGNED (sai_task_t, build_step_count, "build_step_count"),
};
const lws_struct_map_t lsm_schema_json_map_task[] = {
diff --git a/src/server/s-notification.c b/src/server/s-notification.c
index cf1019d..f4c3930 100644
--- a/src/server/s-notification.c
+++ b/src/server/s-notification.c
@@ -63,6 +63,7 @@ static const char * const saifile_paths[] = {
"schema",
"platforms.*.build[]",
"platforms.*.build",
+ "platforms.*.build.*",
"platforms.*.default",
"platforms.*",
"configurations.*.prep",
@@ -78,6 +79,7 @@ enum enum_saifile_paths {
LEJPNSAIF_SCHEMA,
LEJPNSAIF_PLAT_BUILD_STAGE,
LEJPNSAIF_PLAT_BUILD,
+ LEJPNSAIF_PLAT_BUILD_ELEMENT,
LEJPNSAIF_PLAT_DEFAULT,
LEJPNSAIF_PLAT_NAME,
LEJPNSAIF_CONFIGURATIONS_PREP,
@@ -502,6 +504,21 @@ sai_saifile_lejp_cb(struct lejp_ctx *ctx, char reason)
return -1;
}
+ pss->sn.t.build_step = 0;
+ pss->sn.t.build_step_count = 0;
+ if (pss->sn.t.build[0]) {
+ const char *p = pss->sn.t.build;
+ pss->sn.t.build_step_count = 1;
+ while ((p = strchr(p, '\n'))) {
+ pss->sn.t.build_step_count++;
+ p++;
+ }
+ }
+
+ lwsl_notice("Task %s: step_count %d\n",
+ pss->sn.t.taskname,
+ pss->sn.t.build_step_count);
+
/*
* Prepare a struct of the task object...
* task uuid is the event uuid and another
@@ -713,6 +730,18 @@ sai_saifile_lejp_cb(struct lejp_ctx *ctx, char reason)
}
break;
+ case LEJPNSAIF_PLAT_BUILD_ELEMENT:
+ n = strlen(sn->platbuild);
+ if (n) {
+ if (n > sizeof(sn->platbuild) - 2)
+ break;
+ sn->platbuild[n++] = '\n';
+ }
+ if (n < sizeof(sn->platbuild) - 2)
+ lws_strnncpy(sn->platbuild + n, ctx->buf, ctx->npos,
+ sizeof(sn->platbuild) - n);
+ break;
+
case LEJPNSAIF_PLAT_DEFAULT:
sn->nondefault = !arg_to_bool(ctx->buf);
break;
diff --git a/src/server/s-task.c b/src/server/s-task.c
index 46f19b8..982855e 100644
--- a/src/server/s-task.c
+++ b/src/server/s-task.c
@@ -773,6 +773,24 @@ sais_task_reset(struct vhd *vhd, const char *task_uuid)
sais_event_db_close(vhd, &pdb);
+ if (sais_event_db_ensure_open(vhd, event_uuid, 0, &pdb)) {
+ lwsl_err("%s: unable to open event-specific database for reset\n",
+ __func__);
+ return SAI_DB_RESULT_ERROR;
+ }
+ lws_snprintf(cmd, sizeof(cmd), "update tasks set build_step=0 where uuid='%s'",
+ esc);
+ ret = sqlite3_exec(pdb, cmd, NULL, NULL, NULL);
+ if (ret != SQLITE_OK) {
+ sais_event_db_close(vhd, &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;
+ }
+ sais_event_db_close(vhd, &pdb);
+
sais_set_task_state(vhd, NULL, NULL, task_uuid, SAIES_WAITING, 1, 1);
sais_task_cancel(vhd, task_uuid);
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 73b07fd..29deb0e 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -265,6 +265,31 @@ sais_log_to_db(struct vhd *vhd, sai_log_t *log)
}
ot->last_log_timestamp = lws_now_usecs();
+
+ if (log->channel == 3 && log->log) { /* control channel */
+ int step;
+ if (sscanf(log->log, " Step %d OK:", &step) == 1) {
+ char event_uuid[33];
+ sqlite3 *pdb = NULL;
+ char q[256], esc_uuid[129];
+
+ sai_task_uuid_to_event_uuid(event_uuid, log->task_uuid);
+ if (!sais_event_db_ensure_open(vhd, event_uuid, 0, &pdb)) {
+
+ 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);
+ if (sai_sqlite3_statement(pdb, q, "update build_step"))
+ lwsl_err("%s: failed to update build_step\n", __func__);
+
+ sais_event_db_close(vhd, &pdb);
+
+ sais_taskchange(vhd->h_ss_websrv, log->task_uuid, SAIES_BEING_BUILT);
+ }
+ }
+ }
}
sai_plat_t *