Browse Source

web: background recipe-repo re-sync loop (--repo-sync-interval N, default 10, 0 disables) — the dashboard stays fresh without a restart; the add-on entrypoint loop becomes redundant

Alexander Korolev 1 week ago
parent
commit
bc198f71e1
3 changed files with 39 additions and 1 deletions
  1. 7 0
      src/cli.rs
  2. 11 1
      src/main.rs
  3. 21 0
      src/web/mod.rs

+ 7 - 0
src/cli.rs

@@ -344,6 +344,13 @@ pub enum Commands {
         /// Open the browser once the server is up
         #[arg(long)]
         open: bool,
+
+        /// Re-sync the configured `[[recipe_repos]]` in the background every
+        /// N minutes so the dashboard serves fresh recipes without a restart
+        /// (0 disables; default 10). The HA add-on relies on this instead of
+        /// its own entrypoint loop.
+        #[arg(long, default_value_t = 10)]
+        repo_sync_interval: u32,
     },
 
     /// Table of all tested recipes and their benchmark history

+ 11 - 1
src/main.rs

@@ -279,8 +279,18 @@ async fn main() -> Result<()> {
             token,
             open,
             insecure,
+            repo_sync_interval,
         } => {
-            crate::web::run(config, port, bind, token, open, insecure).await?;
+            crate::web::run(
+                config,
+                port,
+                bind,
+                token,
+                open,
+                insecure,
+                repo_sync_interval,
+            )
+            .await?;
         }
         Commands::Exec {
             cluster,

+ 21 - 0
src/web/mod.rs

@@ -302,6 +302,7 @@ pub async fn run(
     token: Option<String>,
     open: bool,
     insecure: bool,
+    repo_sync_interval: u32,
 ) -> anyhow::Result<()> {
     let addr: SocketAddr = format!("{bind}:{port}").parse()?;
     if !is_loopback(addr) && token.is_none() && !insecure {
@@ -315,6 +316,7 @@ pub async fn run(
         );
     }
     let (tx, _) = broadcast::channel(BUS_CAP);
+    let cfg = config.clone(); // for the background recipe-repo re-sync
     let app = AppState {
         config: Arc::new(config),
         bus: tx,
@@ -326,6 +328,25 @@ pub async fn run(
     };
     jobs::load(&app); // history survives restarts; in-flight jobs -> failed
 
+    // Background recipe-repo re-sync (the initial sync already ran at command
+    // start): keeps the dashboard's recipe view fresh without a restart.
+    if repo_sync_interval > 0 {
+        let minutes = repo_sync_interval;
+        tokio::spawn(async move {
+            loop {
+                tokio::time::sleep(Duration::from_secs(minutes as u64 * 60)).await;
+                let roots = crate::recipe_repos::sync_recipe_repos(&cfg).await;
+                tracing::info!(
+                    "{}",
+                    fluent_i18n::t!("recipe-repo-watch", {
+                        "n" => roots.len(),
+                        "interval" => minutes
+                    })
+                );
+            }
+        });
+    }
+
     tokio::spawn(supervisor(app.clone()));
     let listener = tokio::net::TcpListener::bind(addr).await?;
     if open {