Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
Rework the generated HTML pages to be less walls of text
Merged liw opened 1 year ago
2 files changed +202 -207 7a11bedc e58d4c3a
modified src/pages.rs
@@ -26,7 +26,7 @@ use radicle::prelude::RepoId;
use crate::{
    db::{Db, DbError},
    event::BrokerEvent,
-
    msg::{RunId, RunResult},
+
    msg::RunId,
    notif::NotificationReceiver,
    run::{Run, RunState, Whence},
};
@@ -116,27 +116,12 @@ impl PageData {
    fn status_page_as_html(&self) -> Result<HtmlPage, PageError> {
        let mut doc = HtmlPage::default();

-
        doc.push_to_head(
-
            Element::new(Tag::Title)
-
                .with_text("CI for Radicle node ")
-
                .with_text(&self.node_alias),
-
        );
-

-
        doc.push_to_head(Element::new(Tag::Style).with_text(CSS));
-

-
        doc.push_to_head(
-
            Element::new(Tag::Meta)
-
                .with_attribute("http-equiv", "refresh")
-
                .with_attribute("content", REFERESH_INTERVAL),
-
        );
+
        let title = format!("CI for Radicle node {}", self.node_alias);
+
        Self::head(&mut doc, &title);

-
        doc.push_to_body(
-
            Element::new(Tag::H1)
-
                .with_text("CI for Radicle node ")
-
                .with_text(&self.node_alias),
-
        );
+
        doc.push_to_body(Element::new(Tag::H1).with_text(&title));

-
        doc.push_to_body(Element::new(Tag::H2).with_text("Broker status"));
+
        Self::h1(&mut doc, "Broker status");
        doc.push_to_body(
            Element::new(Tag::P)
                .with_text("Last updated: ")
@@ -149,111 +134,192 @@ impl PageData {
                .with_text(")"),
        );

+
        Self::h1(&mut doc, "Repositories");
+
        Self::p_text(&mut doc, "Latest CI run for each repository.");
+

+
        let mut table = Element::new(Tag::Table).with_class("repolist").with_child(
+
            Element::new(Tag::Tr)
+
                .with_child(Element::new(Tag::Th).with_text("Repository"))
+
                .with_child(Element::new(Tag::Th).with_text("Run ID"))
+
                .with_child(Element::new(Tag::Th).with_text("Status"))
+
                .with_child(Element::new(Tag::Th).with_text("Info")),
+
        );
+

+
        for (alias, rid) in self.repos() {
+
            let (run_ids, status, info_url) = match self.latest_run(rid) {
+
                Some(run) => (
+
                    Self::run_ids(Some(run)),
+
                    run.state().to_string(),
+
                    Self::info_url(Some(run)),
+
                ),
+
                None => (
+
                    Self::run_ids(None),
+
                    "unknown".to_string(),
+
                    Self::info_url(None),
+
                ),
+
            };
+

+
            table.push_child(
+
                Element::new(Tag::Tr)
+
                    .with_child(Element::new(Tag::Td).with_child(Self::repository(rid, &alias)))
+
                    .with_child(Element::new(Tag::Td).with_child(run_ids))
+
                    .with_child(
+
                        Element::new(Tag::Td).with_child(
+
                            Element::new(Tag::Span)
+
                                .with_class("run-status")
+
                                .with_text(&status),
+
                        ),
+
                    )
+
                    .with_child(Element::new(Tag::Td).with_child(info_url)),
+
            );
+
        }
+
        doc.push_to_body(table);
+

+
        Self::h1(&mut doc, "Recent status");
        let status = StatusData::from(self).as_json()?;
        doc.push_to_body(
-
            Element::new(Tag::P).with_child(
-
                Element::new(Tag::A)
-
                    .with_attribute("href", STATUS_JSON)
-
                    .with_text(STATUS_JSON),
-
            ),
+
            Element::new(Tag::P)
+
                .with_text("See also as a separate file ")
+
                .with_child(
+
                    Element::new(Tag::A)
+
                        .with_attribute("href", STATUS_JSON)
+
                        .with_child(Element::new(Tag::Code).with_text(STATUS_JSON)),
+
                )
+
                .with_text(": "),
        );
        doc.push_to_body(
            Element::new(Tag::Blockquote).with_child(Element::new(Tag::Pre).with_text(&status)),
        );

-
        doc.push_to_body(Element::new(Tag::H2).with_text("Repositories"));
+
        Ok(doc)
+
    }

-
        doc.push_to_body(Element::new(Tag::P).with_text("Latest CI run for each repository."));
+
    fn per_repo_page_as_html(&self, rid: RepoId, alias: &str, timestamp: &str) -> HtmlPage {
+
        let mut doc = HtmlPage::default();

-
        let mut list = Element::new(Tag::Ul).with_class("repolist");
-
        for (alias, rid) in self.repos() {
-
            let mut item = Element::new(Tag::Li);
+
        let title = format!("CI runs for repository {}", alias);
+
        Self::head(&mut doc, &title);

-
            let runs = self.runs(rid);
-
            item.push_child(
-
                Element::new(Tag::Span).with_child(
-
                    Element::new(Tag::A)
-
                        .with_attribute("href", &format!("{}.html", rid_to_basename(rid)))
-
                        .with_text("Repository ")
-
                        .with_child(
-
                            Element::new(Tag::Span)
-
                                .with_class("alias)")
-
                                .with_text(&alias),
-
                        )
-
                        .with_text(" (")
-
                        .with_child(
-
                            Element::new(Tag::Code)
-
                                .with_class("repoid)")
-
                                .with_text(&rid.to_string()),
-
                        )
-
                        .with_text(&format!("), {} runs", runs.len())),
-
                ),
-
            );
+
        Self::h1(&mut doc, &title);
+
        doc.push_to_body(
+
            Element::new(Tag::P).with_text("Repository ID ").with_child(
+
                Element::new(Tag::Code)
+
                    .with_class("repoid")
+
                    .with_text(&rid.to_string()),
+
            ),
+
        );
+
        Self::p_text(&mut doc, &format!("Last updated: {timestamp}"));
+

+
        let mut table = Element::new(Tag::Table).with_class("run-list").with_child(
+
            Element::new(Tag::Tr)
+
                .with_child(Element::new(Tag::Th).with_text("Run ID"))
+
                .with_child(Element::new(Tag::Th).with_text("Whence"))
+
                .with_child(Element::new(Tag::Th).with_text("Status"))
+
                .with_child(Element::new(Tag::Th).with_text("Info")),
+
        );

-
            if let Some(run) = self.latest_run(rid) {
-
                item.push_child(Element::new(Tag::Br));
-
                item.push_child(
+
        let mut runs = self.runs(rid);
+
        runs.sort_by_cached_key(|run| run.timestamp());
+
        runs.reverse();
+
        for run in runs {
+
            let current = match run.state() {
+
                RunState::Triggered => Element::new(Tag::Span)
+
                    .with_attribute("state", "triggered")
+
                    .with_text("triggered"),
+
                RunState::Running => Element::new(Tag::Span)
+
                    .with_class("running)")
+
                    .with_text("running"),
+
                RunState::Finished => {
+
                    let result = if let Some(result) = run.result() {
+
                        result.to_string()
+
                    } else {
+
                        "unknown".into()
+
                    };
                    Element::new(Tag::Span)
-
                        .with_text("latest run at ")
-
                        .with_text(run.timestamp())
-
                        .with_child(Element::new(Tag::Br))
-
                        .with_text("broker run ID: ")
-
                        .with_child(
-
                            Element::new(Tag::Code)
-
                                .with_class("broker_run_id")
-
                                .with_text(run.broker_run_id().as_str()),
-
                        )
-
                        .with_child(Element::new(Tag::Br))
-
                        .with_text("adapter run ID: ")
-
                        .with_child(if let Some(id) = run.adapter_run_id() {
-
                            Element::new(Tag::Code)
-
                                .with_class("adapter_run_id")
-
                                .with_text(id.as_str())
-
                        } else {
-
                            Element::new(Tag::Span).with_text("unknown")
-
                        })
-
                        .with_child(Element::new(Tag::Br))
-
                        .with_text(" ")
-
                        .with_child(Self::whence_as_html(run.whence())),
-
                );
-
                item.push_child(Element::new(Tag::Br));
-

-
                let state = run.state().to_string();
-
                item.push_child(Element::new(Tag::Span).with_class(&state).with_text(&state));
-

-
                let result = match run.result() {
-
                    None => Element::new(Tag::Span)
-
                        .with_text(" ")
-
                        .with_class("unknown")
-
                        .with_text("unknown result"),
-
                    Some(RunResult::Success) => Element::new(Tag::Span)
-
                        .with_text(" ")
-
                        .with_class("success")
-
                        .with_text("success"),
-
                    Some(_) => Element::new(Tag::Span)
-
                        .with_text(" ")
-
                        .with_class("failure")
-
                        .with_text("failure"),
-
                };
-

-
                item.push_child(result);
-

-
                if let Some(url) = run.adapter_info_url() {
-
                    item.push_child(Element::new(Tag::Br));
-
                    let log = Element::new(Tag::Span).with_child(
-
                        Element::new(Tag::A)
-
                            .with_attribute("href", url)
-
                            .with_text("log"),
-
                    );
-
                    item.push_child(log);
+
                        .with_class(&result)
+
                        .with_text(&result)
                }
-
            }
+
            };

-
            list.push_child(item);
+
            table.push_child(
+
                Element::new(Tag::Tr)
+
                    .with_child(Element::new(Tag::Td).with_child(Self::run_ids(Some(run))))
+
                    .with_child(
+
                        Element::new(Tag::Td).with_child(Self::whence_as_html(run.whence())),
+
                    )
+
                    .with_child(Element::new(Tag::Td).with_child(current))
+
                    .with_child(Element::new(Tag::Td).with_child(Self::info_url(Some(run)))),
+
            );
        }
-
        doc.push_to_body(list);

-
        Ok(doc)
+
        doc.push_to_body(table);
+

+
        doc
+
    }
+

+
    fn head(page: &mut HtmlPage, title: &str) {
+
        page.push_to_head(Element::new(Tag::Title).with_text(title));
+
        page.push_to_head(Element::new(Tag::Style).with_text(CSS));
+
        page.push_to_head(
+
            Element::new(Tag::Meta)
+
                .with_attribute("http-equiv", "refresh")
+
                .with_attribute("content", REFERESH_INTERVAL),
+
        );
+
    }
+

+
    fn h1(page: &mut HtmlPage, text: &str) {
+
        page.push_to_body(Element::new(Tag::H2).with_text(text));
+
    }
+

+
    fn p_text(page: &mut HtmlPage, text: &str) {
+
        page.push_to_body(Element::new(Tag::P).with_text(text));
+
    }
+

+
    fn repository(repo_id: RepoId, alias: &str) -> Element {
+
        Element::new(Tag::A)
+
            .with_child(
+
                Element::new(Tag::Code)
+
                    .with_class("alias)")
+
                    .with_text(alias),
+
            )
+
            .with_attribute("href", &format!("{}.html", rid_to_basename(repo_id)))
+
    }
+

+
    fn run_ids(run: Option<&Run>) -> Element {
+
        if let Some(run) = run {
+
            let adapter_run_id = if let Some(x) = run.adapter_run_id() {
+
                Element::new(Tag::Span).with_text(x.as_str())
+
            } else {
+
                Element::new(Tag::Span)
+
            };
+

+
            Element::new(Tag::Span).with_child(
+
                Element::new(Tag::Span)
+
                    .with_class("adapter-run-id")
+
                    .with_child(adapter_run_id)
+
                    .with_child(Element::new(Tag::Br))
+
                    .with_child(
+
                        Element::new(Tag::Span)
+
                            .with_class("broker-run-id")
+
                            .with_text(&run.broker_run_id().to_string()),
+
                    )
+
                    .with_child(Element::new(Tag::Br))
+
                    .with_text(run.timestamp()),
+
            )
+
        } else {
+
            Element::new(Tag::Span)
+
        }
+
    }
+

+
    fn info_url(run: Option<&Run>) -> Element {
+
        if let Some(run) = run {
+
            if let Some(url) = run.adapter_info_url() {
+
                return Element::new(Tag::A)
+
                    .with_attribute("href", url)
+
                    .with_text("info");
+
            }
+
        }
+
        Element::new(Tag::Span)
    }

    fn whence_as_html(whence: &Whence) -> Element {
@@ -320,104 +386,6 @@ impl PageData {
        }
    }

-
    fn per_repo_page_as_html(&self, rid: RepoId, alias: &str, timestamp: &str) -> HtmlPage {
-
        let mut doc = HtmlPage::default();
-

-
        doc.push_to_head(
-
            Element::new(Tag::Title)
-
                .with_text("CI runs for repository ")
-
                .with_text(alias),
-
        );
-

-
        doc.push_to_head(Element::new(Tag::Style).with_text(CSS));
-

-
        doc.push_to_body(
-
            Element::new(Tag::H1)
-
                .with_text("CI runs for repository ")
-
                .with_text(alias),
-
        );
-

-
        doc.push_to_body(
-
            Element::new(Tag::P)
-
                .with_text("Last updated: ")
-
                .with_text(timestamp),
-
        );
-

-
        doc.push_to_body(
-
            Element::new(Tag::P)
-
                .with_text("Repository ID ")
-
                .with_child(Element::new(Tag::Code).with_text(&rid.to_string())),
-
        );
-

-
        let mut runs = self.runs(rid);
-
        runs.sort_by_cached_key(|run| run.timestamp());
-
        runs.reverse();
-
        let mut list = Element::new(Tag::Ol).with_class("runlist");
-
        for run in runs {
-
            let current = match run.state() {
-
                RunState::Triggered => Element::new(Tag::Span)
-
                    .with_attribute("state", "triggered")
-
                    .with_text("triggered"),
-
                RunState::Running => Element::new(Tag::Span)
-
                    .with_class("running)")
-
                    .with_text("running"),
-
                RunState::Finished => {
-
                    let result = if let Some(result) = run.result() {
-
                        result.to_string()
-
                    } else {
-
                        "unknown".into()
-
                    };
-
                    Element::new(Tag::Span)
-
                        .with_class(&result)
-
                        .with_text(&result)
-
                }
-
            };
-

-
            let info_url = if let Some(url) = run.adapter_info_url() {
-
                Element::new(Tag::Span).with_child(
-
                    Element::new(Tag::A)
-
                        .with_attribute("href", url)
-
                        .with_text("info from adapter"),
-
                )
-
            } else {
-
                Element::new(Tag::Span)
-
                    .with_child(Element::new(Tag::Span).with_text("no info from adapter"))
-
            };
-

-
            list.push_child(
-
                Element::new(Tag::Li)
-
                    .with_text("at ")
-
                    .with_text(run.timestamp())
-
                    .with_text("; currently ")
-
                    .with_child(current)
-
                    .with_text("; ")
-
                    .with_child(info_url)
-
                    .with_child(Element::new(Tag::Br))
-
                    .with_text("broker run ID: ")
-
                    .with_child(
-
                        Element::new(Tag::Code)
-
                            .with_class("broker_run_id")
-
                            .with_text(run.broker_run_id().as_str()),
-
                    )
-
                    .with_child(Element::new(Tag::Br))
-
                    .with_text("adapter run ID: ")
-
                    .with_child(if let Some(id) = run.adapter_run_id() {
-
                        Element::new(Tag::Code)
-
                            .with_class("adapter_run_id")
-
                            .with_text(id.as_str())
-
                    } else {
-
                        Element::new(Tag::Span).with_text("unknown")
-
                    })
-
                    .with_child(Element::new(Tag::Br))
-
                    .with_child(Self::whence_as_html(run.whence())),
-
            );
-
        }
-

-
        doc.push_to_body(list);
-

-
        doc
-
    }
-

    fn repos(&self) -> Vec<(String, RepoId)> {
        let rids: HashSet<(String, RepoId)> = self
            .runs
modified src/radicle-ci.css
@@ -46,6 +46,14 @@ code.repoid {
    font-weight: bold;
}

+
span.broker_run_id {
+
    font-style: monospace;
+
}
+

+
span.adapter_run_id {
+
    font-style: monospace;
+
}
+

span.triggered {
    font-weight: bold;
}
@@ -60,3 +68,22 @@ span.finished {
span.who {
    font-family: monospace;
}
+

+
table {
+
    width: 90%;
+
    table-layout: fixed;
+
}
+

+
td, th {
+
    text-align: left;
+
    vertical-align: top;
+
    padding-top: 0.5em;
+
    padding-bottom: 0.5em;
+
}
+

+
tr {
+
}
+

+
tr:nth-child(even) {
+
    background-color: #f2f2f2;
+
}