summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2023-06-24 20:22:21 -0700
committeriximeow <me@iximeow.net>2023-06-24 20:22:21 -0700
commit916965ab13d4c9273089162a04d730f3061c6d18 (patch)
tree806f2c5dfaa9698402dee8b76c953ba4e4e39776 /src
parent7b08cb229de7b0c131bc8c7e3405ad548ff5e0eb (diff)
and now the index page is useful
Diffstat (limited to 'src')
-rw-r--r--src/dbctx.rs14
-rw-r--r--src/main.rs73
2 files changed, 73 insertions, 14 deletions
diff --git a/src/dbctx.rs b/src/dbctx.rs
index 476c3fc..c9454fc 100644
--- a/src/dbctx.rs
+++ b/src/dbctx.rs
@@ -221,6 +221,20 @@ impl DbCtx {
.map_err(|e| e.to_string())
}
+ pub fn remote_by_id(&self, id: u64) -> Result<Option<Remote>, String> {
+ self.conn.lock()
+ .unwrap()
+ .query_row("select * from remotes where id=?1", [id], |row| {
+ let (id, repo_id, remote_path, remote_api, remote_url, remote_git_url, notifier_config_path) = row.try_into().unwrap();
+
+ Ok(Remote {
+ id, repo_id, remote_path, remote_api, remote_url, remote_git_url, notifier_config_path
+ })
+ })
+ .optional()
+ .map_err(|e| e.to_string())
+ }
+
pub fn repo_id_by_remote(&self, remote_id: u64) -> Result<Option<u64>, String> {
self.conn.lock()
.unwrap()
diff --git a/src/main.rs b/src/main.rs
index 7f89ced..862e021 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -110,6 +110,34 @@ fn duration_as_human_string(duration_ms: u64) -> String {
}
}
+/// try producing a url for whatever caused this job to be started, if possible
+fn commit_url(job: &Job, commit_sha: &str, ctx: &Arc<DbCtx>) -> Option<String> {
+ let remote = ctx.remote_by_id(job.remote_id).expect("query succeeds").expect("existing job references existing remote");
+
+ match remote.remote_api.as_str() {
+ "github" => {
+ Some(format!("{}/commit/{}", remote.remote_url, commit_sha))
+ },
+ "email" => {
+ None
+ },
+ _ => {
+ None
+ }
+ }
+}
+
+/// produce a url to the ci.butactuallyin.space job details page
+fn job_url(job: &Job, commit_sha: &str, ctx: &Arc<DbCtx>) -> String {
+ let remote = ctx.remote_by_id(job.remote_id).expect("query succeeds").expect("existing job references existing remote");
+
+ if remote.remote_api != "github" {
+ eprintln!("job url for remote type {} can't be constructed, i think", &remote.remote_api);
+ }
+
+ format!("{}/{}", &remote.remote_path, commit_sha)
+}
+
fn parse_push_event(body: serde_json::Value) -> Result<GithubEvent, GithubHookError> {
let body = body.as_object()
.ok_or(GithubHookError::BodyNotObject)?;
@@ -231,13 +259,19 @@ async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
Ok(repos) => repos,
Err(e) => {
eprintln!("failed to get repos: {:?}", e);
- return (StatusCode::INTERNAL_SERVER_ERROR, "gonna feel that one tomorrow".to_string());
+ return (StatusCode::INTERNAL_SERVER_ERROR, Html("gonna feel that one tomorrow".to_string()));
}
};
let mut response = String::new();
response.push_str("<html>\n");
+ response.push_str("<style>\n");
+ response.push_str(".build-table { font-family: monospace; border: 1px solid black; border-collapse: collapse; }\n");
+ response.push_str(".row-item { padding-left: 4px; padding-right: 4px; border-right: 1px solid black; }\n");
+ response.push_str(".odd-row { background: #eee; }\n");
+ response.push_str(".even-row { background: #ddd; }\n");
+ response.push_str("</style>\n");
response.push_str("<h1>builds and build accessories</h1>\n");
match repos.len() {
@@ -246,14 +280,16 @@ async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
other => { response.push_str(&format!("<p>{} repos configured</p>\n", other)); },
}
- response.push_str("<table>\n");
+ response.push_str("<table class='build-table'>");
response.push_str("<tr>\n");
- let headings = ["repo", "last build", "build commit", "duration", "status", "result"];
+ let headings = ["repo", "last build", "job", "build commit", "duration", "status", "result"];
for heading in headings {
- response.push_str(&format!("<th>{}</th>", heading));
+ response.push_str(&format!("<th class='row-item'>{}</th>", heading));
}
response.push_str("</tr>\n");
+ let mut row_num = 0;
+
for repo in repos {
let mut most_recent_job: Option<Job> = None;
@@ -269,6 +305,12 @@ async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
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 commit_html = match commit_url(&job, &job_commit, &ctx) {
+ Some(url) => format!("<a href=\"{}\">{}</a>", url, &job_commit),
+ None => job_commit.clone()
+ };
+
+ let job_html = format!("<a href=\"{}\">{}</a>", job_url(&job, &job_commit, &ctx), job.id);
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 {
@@ -293,20 +335,18 @@ async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
Some(_) => "<span style='color:red;'>fail</span>",
None => match job.state {
JobState::Pending => { "unstarted" },
- JobState::Started => { "<span style='color:yellow;'>in progress</span>" },
+ JobState::Started => { "<span style='color:darkgoldenrod;'>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 = [repo.name.as_str(), last_build_time.as_str(), job_html.as_str(), commit_html.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(&format!("<td class='row-item'>{}</td>", entry));
}
- row_html.push_str("</tr>");
row_html
}
None => {
@@ -314,21 +354,26 @@ async fn handle_ci_index(State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
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(&format!("<td class='row-item'>{}</td>", entry));
}
- row_html.push_str("</tr>");
row_html
}
};
+ let row_index = row_num % 2;
+ response.push_str(&format!("<tr class=\"{}\">", ["even-row", "odd-row"][row_index]));
response.push_str(&row_html);
+ response.push_str("</tr>");
+ response.push('\n');
+
+ row_num += 1;
}
+ response.push_str("</table>");
response.push_str("</html>");
- (StatusCode::OK, response)
+ (StatusCode::OK, Html(response))
}
async fn handle_commit_status(Path(path): Path<(String, String, String)>, State(ctx): State<Arc<DbCtx>>) -> impl IntoResponse {
@@ -366,7 +411,7 @@ async fn handle_commit_status(Path(path): Path<(String, String, String)>, State(
let deployed = false;
- let head = format!("<head><title>ci.butactuallin.space - {}</title></head>", repo_name);
+ let head = format!("<head><title>ci.butactuallyin.space - {}</title></head>", repo_name);
let remote_commit_elem = format!("<a href=\"https://www.github.com/{}/commit/{}\">{}</a>", &remote_path, &sha, &sha);
let status_elem = match state {
JobState::Pending | JobState::Started => {