summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock215
-rw-r--r--Cargo.toml1
-rw-r--r--src/dbctx.rs116
-rw-r--r--src/main.rs145
-rw-r--r--src/sql.rs7
5 files changed, 464 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8c1ad8d..7eaa568 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,21 @@ dependencies = [
]
[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -262,6 +277,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "chrono"
+version = "0.4.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "time",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -347,6 +377,16 @@ dependencies = [
]
[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
name = "color-eyre"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -500,6 +540,50 @@ dependencies = [
]
[[package]]
+name = "cxx"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -760,7 +844,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@@ -992,6 +1076,30 @@ dependencies = [
]
[[package]]
+name = "iana-time-zone"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1088,6 +1196,7 @@ dependencies = [
"axum-macros",
"axum-server",
"base64",
+ "chrono",
"clap 4.0.29",
"console-subscriber",
"futures-util",
@@ -1169,6 +1278,15 @@ dependencies = [
]
[[package]]
+name = "link-cplusplus"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "linux-raw-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1268,7 +1386,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.42.0",
]
@@ -1910,6 +2028,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
+name = "scratch"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
+
+[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2174,6 +2298,17 @@ dependencies = [
]
[[package]]
+name = "time"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
+[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2602,6 +2737,12 @@ dependencies = [
[[package]]
name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
@@ -2724,6 +2865,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2742,22 +2892,43 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
- "windows_aarch64_gnullvm",
+ "windows_aarch64_gnullvm 0.42.0",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
- "windows_x86_64_gnullvm",
+ "windows_x86_64_gnullvm 0.42.0",
"windows_x86_64_msvc 0.42.0",
]
[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2770,6 +2941,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2782,6 +2959,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2794,6 +2977,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2806,12 +2995,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2824,6 +3025,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 6ea03b7..03e5f77 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ sha2 = "*"
reqwest = { version = "*", features = ["rustls-tls-native-roots"] }
clap = { version = "*", features = ["derive"] }
rlua = "*"
+chrono = "*"
[[bin]]
name = "ci_webserver"
diff --git a/src/dbctx.rs b/src/dbctx.rs
index 67740e8..476c3fc 100644
--- a/src/dbctx.rs
+++ b/src/dbctx.rs
@@ -20,6 +20,41 @@ pub struct DbCtx {
}
#[derive(Debug, Clone)]
+pub struct Repo {
+ pub id: u64,
+ pub name: String,
+}
+
+#[derive(Debug)]
+pub struct Remote {
+ pub id: u64,
+ pub repo_id: u64,
+ pub remote_path: String,
+ pub remote_api: String,
+ pub remote_url: String,
+ pub remote_git_url: String,
+ pub notifier_config_path: String,
+}
+
+#[derive(Debug, Clone)]
+pub struct Job {
+ pub id: u64,
+ pub artifacts_path: Option<String>,
+ pub state: sql::JobState,
+ pub run_host: String,
+ pub remote_id: u64,
+ pub commit_id: u64,
+ pub created_time: u64,
+ pub start_time: Option<u64>,
+ pub complete_time: Option<u64>,
+ pub build_token: Option<String>,
+ pub job_timeout: Option<u64>,
+ pub source: Option<String>,
+ pub build_result: Option<u8>,
+ pub final_text: Option<String>,
+}
+
+#[derive(Debug, Clone)]
pub struct PendingJob {
pub id: u64,
pub artifacts: Option<String>,
@@ -131,6 +166,17 @@ impl DbCtx {
ArtifactDescriptor::new(job_id, artifact_id).await
}
+ pub fn commit_sha(&self, commit_id: u64) -> Result<String, String> {
+ self.conn.lock()
+ .unwrap()
+ .query_row(
+ "select sha from commits where id=?1",
+ [commit_id],
+ |row| { row.get(0) }
+ )
+ .map_err(|e| e.to_string())
+ }
+
pub fn job_for_commit(&self, sha: &str) -> Result<Option<u64>, String> {
self.conn.lock()
.unwrap()
@@ -268,6 +314,54 @@ impl DbCtx {
Ok(artifacts)
}
+ pub fn get_repos(&self) -> Result<Vec<Repo>, String> {
+ let conn = self.conn.lock().unwrap();
+
+ let mut repos_query = conn.prepare(sql::ALL_REPOS).unwrap();
+ let mut repos = repos_query.query([]).unwrap();
+ let mut result = Vec::new();
+
+ while let Some(row) = repos.next().unwrap() {
+ let (id, repo_name) = row.try_into().unwrap();
+ result.push(Repo {
+ id,
+ name: repo_name,
+ });
+ }
+
+ Ok(result)
+ }
+
+ pub fn last_job_from_remote(&self, id: u64) -> Result<Option<Job>, String> {
+ let conn = self.conn.lock().unwrap();
+
+ let mut job_query = conn.prepare(sql::LAST_JOB_FROM_REMOTE).unwrap();
+ let mut result = job_query.query([id]).unwrap();
+
+ let job = result.next().expect("can get next row, which may be None").map(|row| {
+ let (id, artifacts_path, state, run_host, remote_id, commit_id, created_time, start_time, complete_time, build_token, job_timeout, source, build_result, final_text) = row.try_into().unwrap();
+ let state: u8 = state;
+ Job {
+ id,
+ artifacts_path,
+ state: state.try_into().unwrap(),
+ run_host,
+ remote_id,
+ commit_id,
+ created_time,
+ start_time,
+ complete_time,
+ build_token,
+ job_timeout,
+ source,
+ build_result,
+ final_text,
+ }
+ });
+
+ Ok(job)
+ }
+
pub fn get_pending_jobs(&self) -> Result<Vec<PendingJob>, String> {
let conn = self.conn.lock().unwrap();
@@ -289,17 +383,7 @@ impl DbCtx {
Ok(pending)
}
- pub fn notifiers_by_repo(&self, repo_id: u64) -> Result<Vec<RemoteNotifier>, String> {
- #[derive(Debug)]
- #[allow(dead_code)]
- struct Remote {
- id: u64,
- repo_id: u64,
- remote_path: String,
- remote_api: String,
- notifier_config_path: String,
- }
-
+ pub fn remotes_by_repo(&self, repo_id: u64) -> Result<Vec<Remote>, String> {
let mut remotes: Vec<Remote> = Vec::new();
let conn = self.conn.lock().unwrap();
@@ -308,11 +392,15 @@ impl DbCtx {
while let Some(row) = remote_results.next().unwrap() {
let (id, repo_id, remote_path, remote_api, remote_url, remote_git_url, notifier_config_path) = row.try_into().unwrap();
- let _: String = remote_url;
- let _: String = remote_git_url;
- remotes.push(Remote { id, repo_id, remote_path, remote_api, notifier_config_path });
+ remotes.push(Remote { id, repo_id, remote_path, remote_api, remote_url, remote_git_url, notifier_config_path });
}
+ Ok(remotes)
+ }
+
+ pub fn notifiers_by_repo(&self, repo_id: u64) -> Result<Vec<RemoteNotifier>, String> {
+ let remotes = self.remotes_by_repo(repo_id)?;
+
let mut notifiers: Vec<RemoteNotifier> = Vec::new();
for remote in remotes.into_iter() {
diff --git a/src/main.rs b/src/main.rs
index 9c16762..7f89ced 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,6 +2,7 @@
#![allow(unused_variables)]
#![allow(unused_imports)]
+use chrono::{Utc, TimeZone};
use lazy_static::lazy_static;
use std::sync::RwLock;
use serde_derive::{Deserialize, Serialize};
@@ -33,7 +34,7 @@ mod dbctx;
use sql::JobState;
-use dbctx::DbCtx;
+use dbctx::{DbCtx, Job};
use rusqlite::OptionalExtension;
@@ -71,6 +72,44 @@ enum GithubEvent {
Other {}
}
+/// return a duration rendered as the largest two non-zero units.
+///
+/// 60000ms -> 1m
+/// 60001ms -> 1m
+/// 61000ms -> 1m1s
+/// 1030ms -> 1.03s
+fn duration_as_human_string(duration_ms: u64) -> String {
+ let duration_sec = duration_ms / 1000;
+ let duration_min = duration_sec / 60;
+ let duration_hours = duration_min / 60;
+
+ let duration_ms = duration_ms % 1000;
+ let duration_sec = duration_sec & 60;
+ let duration_min = duration_hours & 60;
+ // no need to clamp hours, we're gonna just hope that it's a reasonable number of hours
+
+ if duration_hours != 0 {
+ let mut res = format!("{}h", duration_hours);
+ if duration_min != 0 {
+ res.push_str(&format!("{}m", duration_min));
+ }
+ res
+ } else if duration_min != 0 {
+ let mut res = format!("{}m", duration_min);
+ if duration_min != 0 {
+ res.push_str(&format!("{}s", duration_sec));
+ }
+ res
+ } else {
+ let mut res = format!("{}", duration_sec);
+ if duration_ms != 0 {
+ res.push_str(&format!(".{:03}", duration_ms));
+ }
+ res.push('s');
+ res
+ }
+}
+
fn parse_push_event(body: serde_json::Value) -> Result<GithubEvent, GithubHookError> {
let body = body.as_object()
.ok_or(GithubHookError::BodyNotObject)?;
@@ -187,7 +226,109 @@ async fn handle_github_event(ctx: Arc<DbCtx>, owner: String, repo: String, event
}
async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
- "hello and welcome to my websight"
+ eprintln!("root index");
+ let repos = match ctx.get_repos() {
+ Ok(repos) => repos,
+ Err(e) => {
+ eprintln!("failed to get repos: {:?}", e);
+ return (StatusCode::INTERNAL_SERVER_ERROR, "gonna feel that one tomorrow".to_string());
+ }
+ };
+
+ let mut response = String::new();
+
+ response.push_str("<html>\n");
+ response.push_str("<h1>builds and build accessories</h1>\n");
+
+ match repos.len() {
+ 0 => { response.push_str(&format!("<p>no repos configured, so there are no builds</p>\n")); },
+ 1 => { response.push_str("<p>1 repo configured</p>\n"); },
+ other => { response.push_str(&format!("<p>{} repos configured</p>\n", other)); },
+ }
+
+ response.push_str("<table>\n");
+ response.push_str("<tr>\n");
+ let headings = ["repo", "last build", "build commit", "duration", "status", "result"];
+ for heading in headings {
+ response.push_str(&format!("<th>{}</th>", heading));
+ }
+ response.push_str("</tr>\n");
+
+ for repo in repos {
+ let mut most_recent_job: Option<Job> = None;
+
+ for remote in ctx.remotes_by_repo(repo.id).expect("remotes by repo works") {
+ let last_job = ctx.last_job_from_remote(remote.id).expect("job by remote works");
+ if let Some(last_job) = last_job {
+ if most_recent_job.as_ref().map(|job| job.created_time < last_job.created_time).unwrap_or(true) {
+ most_recent_job = Some(last_job);
+ }
+ }
+ }
+
+ let row_html: String = match most_recent_job {
+ Some(job) => {
+ let job_commit = ctx.commit_sha(job.commit_id).expect("job has a commit");
+
+ let last_build_time = Utc.timestamp_millis_opt(job.created_time as i64).unwrap().to_rfc2822();
+ let duration = if let Some(start_time) = job.start_time {
+ if let Some(complete_time) = job.complete_time {
+ let duration_ms = complete_time - start_time;
+ let duration = duration_as_human_string(duration_ms);
+ duration
+ } else {
+ let now_ms = SystemTime::now().duration_since(UNIX_EPOCH).expect("now is after then").as_millis() as u64;
+ let mut duration = duration_as_human_string(now_ms - start_time);
+ duration.push_str(" (ongoing)");
+ duration
+ }
+ } else {
+ "not yet run".to_owned()
+ };
+
+ let status = format!("{:?}", job.state).to_lowercase();
+
+ let result = match job.build_result {
+ Some(0) => "<span style='color:green;'>pass</span>",
+ Some(_) => "<span style='color:red;'>fail</span>",
+ None => match job.state {
+ JobState::Pending => { "unstarted" },
+ JobState::Started => { "<span style='color:yellow;'>in progress</span>" },
+ _ => { "<span style='color:red;'>unreported</span>" }
+ }
+ };
+
+ let entries = [repo.name.as_str(), last_build_time.as_str(), job_commit.as_str(), &duration, &status, &result];
+ let entries = entries.iter().chain(std::iter::repeat(&"")).take(headings.len());
+
+ let mut row_html = String::new();
+ row_html.push_str("<tr>");
+ for entry in entries {
+ row_html.push_str(&format!("<td>{}</td>", entry));
+ }
+ row_html.push_str("</tr>");
+ row_html
+ }
+ None => {
+ let entries = [repo.name.as_str()];
+ let entries = entries.iter().chain(std::iter::repeat(&"")).take(headings.len());
+
+ let mut row_html = String::new();
+ row_html.push_str("<tr>");
+ for entry in entries {
+ row_html.push_str(&format!("<td>{}</td>", entry));
+ }
+ row_html.push_str("</tr>");
+ row_html
+ }
+ };
+
+ response.push_str(&row_html);
+ }
+
+ response.push_str("</html>");
+
+ (StatusCode::OK, response)
}
async fn handle_commit_status(Path(path): Path<(String, String, String)>, State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
diff --git a/src/sql.rs b/src/sql.rs
index 7bd33fd..b262e9a 100644
--- a/src/sql.rs
+++ b/src/sql.rs
@@ -106,3 +106,10 @@ pub const COMMIT_TO_ID: &'static str = "\
pub const REMOTES_FOR_REPO: &'static str = "\
select * from remotes where repo_id=?1;";
+
+pub const ALL_REPOS: &'static str = "\
+ select * from repos;";
+
+pub const LAST_JOB_FROM_REMOTE: &'static str = "\
+ select * from jobs where remote_id=?1 order by created_time desc limit 1;";
+