Commit

Author:

Hash:

Timestamp:

+171 -13 +/-15 browse

Kevin Schoon [me@kevinschoon.com]

e8e0015dea9405c381e2bf435cb521bf39958061

Fri, 26 Sep 2025 14:48:17 +0000 (2 months ago)

hack up some of the ui
1diff --git a/Cargo.lock b/Cargo.lock
2index e986d9c..2a2e3b0 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -284,6 +284,7 @@ dependencies = [
6 "ayllu_config",
7 "ayllu_git",
8 "ayllu_identity",
9+ "build",
10 "bytes",
11 "comrak",
12 "file-mode",
13 diff --git a/ayllu/Cargo.toml b/ayllu/Cargo.toml
14index 56f20ed..0dc5007 100644
15--- a/ayllu/Cargo.toml
16+++ b/ayllu/Cargo.toml
17 @@ -8,6 +8,7 @@ rust-version = "1.83.0"
18 name = "ayllu"
19
20 [dependencies]
21+ build = { path = "../crates/build" }
22 ayllu_api = { path = "../crates/api" }
23 ayllu_cmd = { path = "../crates/cmd" }
24 ayllu_git = { path = "../crates/git" }
25 diff --git a/ayllu/src/config.rs b/ayllu/src/config.rs
26index 99b21b3..0674b2e 100644
27--- a/ayllu/src/config.rs
28+++ b/ayllu/src/config.rs
29 @@ -250,6 +250,11 @@ pub struct Git {
30 }
31
32 #[derive(Deserialize, Serialize, Clone, Debug)]
33+ pub struct Build {
34+ pub job_store: PathBuf,
35+ }
36+
37+ #[derive(Deserialize, Serialize, Clone, Debug)]
38 pub struct Config {
39 #[serde(default = "Config::default_site_name")]
40 pub site_name: String,
41 @@ -286,6 +291,7 @@ pub struct Config {
42 pub lfs: Option<Lfs>,
43 #[serde(default = "Vec::new")]
44 pub identities: Vec<Identity>,
45+ pub build: Option<Build>,
46 }
47
48 impl Configurable for Config {
49 diff --git a/ayllu/src/web2/middleware/repository.rs b/ayllu/src/web2/middleware/repository.rs
50index 9bb229e..187f9d9 100644
51--- a/ayllu/src/web2/middleware/repository.rs
52+++ b/ayllu/src/web2/middleware/repository.rs
53 @@ -39,6 +39,7 @@ pub struct Preamble {
54 pub file_path: Option<PathBuf>,
55 pub latest_commit: Option<Commit>,
56 pub latest_commit_id: Option<String>,
57+ pub job_store: Option<PathBuf>,
58 }
59
60 impl Preamble {
61 @@ -97,6 +98,10 @@ impl Preamble {
62 file_path: file_path.map(PathBuf::from),
63 latest_commit: latest_commit.clone(),
64 latest_commit_id: latest_commit.map(|commit| commit.id),
65+ job_store: system_config
66+ .build
67+ .as_ref()
68+ .map(|cfg| cfg.job_store.to_path_buf()),
69 })
70 }
71
72 diff --git a/ayllu/src/web2/navigation.rs b/ayllu/src/web2/navigation.rs
73index 1b67ddc..70c46e3 100644
74--- a/ayllu/src/web2/navigation.rs
75+++ b/ayllu/src/web2/navigation.rs
76 @@ -23,8 +23,8 @@ pub fn global(current_page: &str, mail_visible: bool) -> Items {
77 nav
78 }
79
80- pub fn primary(current_page: &str, collection: &str, name: &str) -> Items {
81- vec![
82+ pub fn primary(current_page: &str, collection: &str, name: &str, builds_enabled: bool) -> Items {
83+ let mut items = vec![
84 // (
85 // String::from("authors"),
86 // format!("/{}/{}/authors", collection, name,),
87 @@ -50,7 +50,18 @@ pub fn primary(current_page: &str, collection: &str, name: &str) -> Items {
88 format!("/{collection}/{name}/refs"),
89 current_page == "refs",
90 ),
91- ]
92+ ];
93+ if builds_enabled {
94+ items.insert(
95+ 0,
96+ (
97+ String::from("builds"),
98+ format!("/{collection}/{name}/builds"),
99+ current_page == "builds",
100+ ),
101+ );
102+ }
103+ items
104 }
105
106 pub fn subnav(
107 diff --git a/ayllu/src/web2/routes/blob.rs b/ayllu/src/web2/routes/blob.rs
108index 800b3b5..0e176cf 100644
109--- a/ayllu/src/web2/routes/blob.rs
110+++ b/ayllu/src/web2/routes/blob.rs
111 @@ -64,7 +64,12 @@ pub async fn serve(
112 }
113 let blob = blob.unwrap();
114 base.title = preamble.file_name();
115- base.nav_elements = navigation::primary("blob", &preamble.collection_name, &preamble.repo_name);
116+ base.nav_elements = navigation::primary(
117+ "blob",
118+ &preamble.collection_name,
119+ &preamble.repo_name,
120+ preamble.job_store.is_some(),
121+ );
122 let mime_type = mime_guess::from_path(preamble.file_path_string()).first_or_octet_stream();
123 tracing::debug!("rendering blob with mime type: {mime_type}");
124 let mut content: Option<String> = None;
125 diff --git a/ayllu/src/web2/routes/build.rs b/ayllu/src/web2/routes/build.rs
126new file mode 100644
127index 0000000..e109c45
128--- /dev/null
129+++ b/ayllu/src/web2/routes/build.rs
130 @@ -0,0 +1,83 @@
131+ use askama::Template;
132+ use axum::extract::Path;
133+ use axum::{extract::Extension, response::Html};
134+ use ayllu_git::Wrapper;
135+ use build::Status;
136+
137+ use crate::config::Config;
138+ use crate::web2::error::Error;
139+ use crate::web2::middleware::repository::Preamble;
140+ use crate::web2::navigation;
141+ use crate::web2::template::Base;
142+
143+ #[derive(askama::Template)]
144+ #[template(path = "build.html")]
145+ struct BuildTemplate {
146+ pub base: Base,
147+ pub status: Status,
148+ }
149+
150+ pub async fn build(
151+ Extension(cfg): Extension<Config>,
152+ Extension(preamble): Extension<Preamble>,
153+ Extension(mut base): Extension<Base>,
154+ Path((_, _, build_id)): Path<(String, String, u32)>,
155+ ) -> Result<Html<String>, Error> {
156+ println!("Build ID {build_id}");
157+ let job_store_repository = match preamble.job_store {
158+ Some(path) => Wrapper::new(path.as_path())?,
159+ None => return Err(Error::ComponentNotEnabled(String::from("Builds"))),
160+ };
161+ let store = build::Store {
162+ collection: &preamble.collection_name,
163+ name: &preamble.repo_name,
164+ repository: &job_store_repository,
165+ email_contact: "fixme@example.org",
166+ };
167+ let status = match store
168+ .read(Some(build_id))
169+ .map_err(|e| Error::Message(format!("Failed to lookup build {e:?}")))?
170+ {
171+ Some(status) => status,
172+ None => return Err(Error::NotFound(format!("Cannot find build {build_id}"))),
173+ };
174+ base.current_time = timeutil::timestamp();
175+ base.nav_elements = navigation::primary(
176+ "builds",
177+ &preamble.collection_name,
178+ &preamble.repo_name,
179+ true,
180+ );
181+ Ok(Html(BuildTemplate { base, status }.render()?))
182+ }
183+
184+ #[derive(askama::Template)]
185+ #[template(path = "builds.html")]
186+ struct BuildsTemplate {
187+ pub base: Base,
188+ }
189+
190+ pub async fn builds(
191+ Extension(cfg): Extension<Config>,
192+ Extension(preamble): Extension<Preamble>,
193+ Extension(mut base): Extension<Base>,
194+ ) -> Result<Html<String>, Error> {
195+ let job_store_repository = match preamble.job_store {
196+ Some(path) => Wrapper::new(path.as_path())?,
197+ None => return Err(Error::ComponentNotEnabled(String::from("Builds"))),
198+ };
199+ let store = build::Store {
200+ collection: &preamble.collection_name,
201+ name: &preamble.repo_name,
202+ repository: &job_store_repository,
203+ email_contact: "fixme@example.org",
204+ };
205+ base.nav_elements = navigation::primary(
206+ "builds",
207+ &preamble.collection_name,
208+ &preamble.repo_name,
209+ true,
210+ );
211+ base.current_time = timeutil::timestamp();
212+ Ok(Html(BuildsTemplate { base }.render()?))
213+ }
214 diff --git a/ayllu/src/web2/routes/commit.rs b/ayllu/src/web2/routes/commit.rs
215index 17597a8..c7e3411 100644
216--- a/ayllu/src/web2/routes/commit.rs
217+++ b/ayllu/src/web2/routes/commit.rs
218 @@ -34,8 +34,12 @@ pub async fn serve(
219 ) -> Result<Html<String>, Error> {
220 let repository = Wrapper::new(preamble.repo_path.as_path())?;
221 base.title = format!("Commit: {commit_id}");
222- base.nav_elements =
223- navigation::primary("commit", &preamble.collection_name, &preamble.repo_name);
224+ base.nav_elements = navigation::primary(
225+ "commit",
226+ &preamble.collection_name,
227+ &preamble.repo_name,
228+ preamble.job_store.is_some(),
229+ );
230 let commit = repository.commit(Some(commit_id.to_string()))?.unwrap();
231 let note = if commit.has_note.is_some_and(|has_note| has_note) {
232 Some(repository.read_note(commit.id.as_str())?)
233 diff --git a/ayllu/src/web2/routes/log.rs b/ayllu/src/web2/routes/log.rs
234index 4b4d750..9088ac9 100644
235--- a/ayllu/src/web2/routes/log.rs
236+++ b/ayllu/src/web2/routes/log.rs
237 @@ -38,8 +38,12 @@ pub async fn serve(
238 params: Query<HashMap<String, String>>,
239 ) -> Result<Html<String>, Error> {
240 let repository = Wrapper::new(preamble.repo_path.as_path())?;
241- base.nav_elements =
242- crate::web2::navigation::primary("log", &preamble.collection_name, &preamble.repo_name);
243+ base.nav_elements = crate::web2::navigation::primary(
244+ "log",
245+ &preamble.collection_name,
246+ &preamble.repo_name,
247+ preamble.job_store.is_some(),
248+ );
249 let subnav_elements = navigation::subnav(
250 "log",
251 &preamble.collection_name,
252 diff --git a/ayllu/src/web2/routes/mod.rs b/ayllu/src/web2/routes/mod.rs
253index f2dca6f..7572af5 100644
254--- a/ayllu/src/web2/routes/mod.rs
255+++ b/ayllu/src/web2/routes/mod.rs
256 @@ -1,6 +1,7 @@
257 pub mod about;
258 pub mod assets;
259 pub mod blob;
260+ pub mod build;
261 pub mod commit;
262 pub mod config;
263 pub mod finger;
264 diff --git a/ayllu/src/web2/routes/refs.rs b/ayllu/src/web2/routes/refs.rs
265index 4669293..1056280 100644
266--- a/ayllu/src/web2/routes/refs.rs
267+++ b/ayllu/src/web2/routes/refs.rs
268 @@ -33,7 +33,12 @@ pub async fn refs(
269 Extension(mut base): Extension<Base>,
270 ) -> Result<Html<String>, Error> {
271 let repository = Wrapper::new(preamble.repo_path.as_path())?;
272- base.nav_elements = navigation::primary("refs", &preamble.collection_name, &preamble.repo_name);
273+ base.nav_elements = navigation::primary(
274+ "refs",
275+ &preamble.collection_name,
276+ &preamble.repo_name,
277+ preamble.job_store.is_some(),
278+ );
279 with_preamble!(base, preamble);
280 Ok(Html(
281 RefsPageTemplate {
282 @@ -64,8 +69,12 @@ pub async fn tag(
283 ) -> Result<Html<String>, Error> {
284 let repository = Wrapper::new(preamble.repo_path.as_path())?;
285 if let Some(tag) = repository.tags()?.iter().find(|tag| tag.name == tag_name) {
286- base.nav_elements =
287- navigation::primary("refs", &preamble.collection_name, &preamble.repo_name);
288+ base.nav_elements = navigation::primary(
289+ "refs",
290+ &preamble.collection_name,
291+ &preamble.repo_name,
292+ preamble.job_store.is_some(),
293+ );
294 with_preamble!(base, preamble);
295 Ok(Html(
296 TagTemplate {
297 diff --git a/ayllu/src/web2/routes/repo.rs b/ayllu/src/web2/routes/repo.rs
298index b32c723..19a21aa 100644
299--- a/ayllu/src/web2/routes/repo.rs
300+++ b/ayllu/src/web2/routes/repo.rs
301 @@ -155,8 +155,12 @@ pub async fn serve(
302 preamble.collection_name, preamble.repo_name
303 );
304
305- base.nav_elements =
306- navigation::primary("project", &preamble.collection_name, &preamble.repo_name);
307+ base.nav_elements = navigation::primary(
308+ "project",
309+ &preamble.collection_name,
310+ &preamble.repo_name,
311+ preamble.job_store.is_some(),
312+ );
313
314 with_preamble!(base, preamble);
315
316 diff --git a/ayllu/src/web2/server.rs b/ayllu/src/web2/server.rs
317index 946e7ec..021ba6c 100644
318--- a/ayllu/src/web2/server.rs
319+++ b/ayllu/src/web2/server.rs
320 @@ -23,6 +23,7 @@ use crate::web2::middleware::sites;
321 use crate::web2::routes::about;
322 use crate::web2::routes::assets;
323 use crate::web2::routes::blob;
324+ use crate::web2::routes::build;
325 use crate::web2::routes::commit;
326 use crate::web2::routes::config;
327 use crate::web2::routes::finger;
328 @@ -132,6 +133,8 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
329 .route("/refs", routing::get(refs::refs))
330 .route("/refs/tag/{tag_id}", routing::get(refs::tag))
331 .route("/refs/archive/{ref_id}", routing::get(refs::archive))
332+ .route("/builds", routing::get(build::builds))
333+ .route("/builds/{build_id}", routing::get(build::build))
334 // git smart http clone
335 // /(HEAD|info/refs|objects/info/.*|git-upload-pack).*$
336 .route("/HEAD", routing::get(git::handle))
337 diff --git a/ayllu/templates/build.html b/ayllu/templates/build.html
338new file mode 100644
339index 0000000..9728094
340--- /dev/null
341+++ b/ayllu/templates/build.html
342 @@ -0,0 +1,17 @@
343+ {% extends "base.html" %}
344+ {% block content %}
345+ <section class="build">
346+ <pre>
347+ Success: {{ status.success }}
348+ Runtime: {{ status.runtime }}
349+ </pre>
350+ {% for workflow in status.workflows %}
351+ <section class="workflow">
352+ {{ workflow.name }}
353+ {% for step in workflow.steps %}
354+ {{ step.runtime }}
355+ {% endfor %}
356+ </section>
357+ {% endfor %}
358+ </section>
359+ {% endblock %}
360 diff --git a/ayllu/templates/builds.html b/ayllu/templates/builds.html
361new file mode 100644
362index 0000000..b080b69
363--- /dev/null
364+++ b/ayllu/templates/builds.html
365 @@ -0,0 +1,4 @@
366+ {% extends "base.html" %}
367+ {% block content %}
368+ BUILDS!!!!
369+ {% endblock %}