From 50c7992b423a87530bcf47fa1ef36c18443e8e70 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Sun, 22 Feb 2026 15:01:34 -0800 Subject: [PATCH] Unify plugin bootstrap and prep vendored assets in CLI release --- .github/workflows/release-cli-npm.yml | 37 +++++++++++++ crates-cli/yaak-cli/src/context.rs | 44 ++++++---------- crates-tauri/yaak-app/src/plugins_ext.rs | 39 ++++---------- crates/yaak-plugins/src/bootstrap.rs | 66 ++++++++++++++++++++++++ crates/yaak-plugins/src/lib.rs | 1 + crates/yaak-plugins/src/manager.rs | 30 +---------- scripts/vendor-plugins.cjs | 3 +- 7 files changed, 131 insertions(+), 89 deletions(-) create mode 100644 crates/yaak-plugins/src/bootstrap.rs diff --git a/.github/workflows/release-cli-npm.yml b/.github/workflows/release-cli-npm.yml index 57fef3b9..8246faf2 100644 --- a/.github/workflows/release-cli-npm.yml +++ b/.github/workflows/release-cli-npm.yml @@ -14,8 +14,39 @@ permissions: contents: read jobs: + prepare-vendored-assets: + name: Prepare vendored plugin assets + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Install dependencies + run: npm ci + + - name: Build plugin assets + run: | + npm run build-plugins + npm run vendor:vendor-plugins + + - name: Upload vendored assets + uses: actions/upload-artifact@v4 + with: + name: vendored-assets + path: | + crates-tauri/yaak-app/vendored/plugin-runtime/index.cjs + crates-tauri/yaak-app/vendored/plugins + if-no-files-found: error + build-binaries: name: Build ${{ matrix.pkg }} + needs: prepare-vendored-assets runs-on: ${{ matrix.runner }} strategy: fail-fast: false @@ -67,6 +98,12 @@ jobs: sudo apt-get update sudo apt-get install -y pkg-config libdbus-1-dev + - name: Download vendored assets + uses: actions/download-artifact@v4 + with: + name: vendored-assets + path: crates-tauri/yaak-app/vendored + - name: Build yaak run: cargo build --locked --release -p yaak-cli --bin yaak --target ${{ matrix.target }} diff --git a/crates-cli/yaak-cli/src/context.rs b/crates-cli/yaak-cli/src/context.rs index e21dd4c3..59b065a6 100644 --- a/crates-cli/yaak-cli/src/context.rs +++ b/crates-cli/yaak-cli/src/context.rs @@ -8,6 +8,7 @@ use yaak_crypto::manager::EncryptionManager; use yaak_models::blob_manager::BlobManager; use yaak_models::db_context::DbContext; use yaak_models::query_manager::QueryManager; +use yaak_plugins::bootstrap; use yaak_plugins::events::PluginContext; use yaak_plugins::manager::PluginManager; @@ -51,38 +52,23 @@ impl CliContext { .expect("Failed to prepare embedded plugin runtime") }); - let plugin_manager = Arc::new( - PluginManager::new( - vendored_plugin_dir, - installed_plugin_dir, - node_bin_path, - plugin_runtime_main, - false, - ) - .await, - ); - + match bootstrap::create_and_initialize_manager( + vendored_plugin_dir, + installed_plugin_dir, + node_bin_path, + plugin_runtime_main, + &query_manager, + &PluginContext::new_empty(), + false, + ) + .await { - let db = query_manager.connect(); - if let Err(err) = plugin_manager.ensure_bundled_plugins_registered(&db).await { - eprintln!("Warning: Failed to register bundled plugins: {err}"); + Ok(plugin_manager) => Some(plugin_manager), + Err(err) => { + eprintln!("Warning: Failed to initialize plugins: {err}"); + None } } - - let plugins = query_manager.connect().list_plugins().unwrap_or_default(); - if !plugins.is_empty() { - let errors = plugin_manager - .initialize_all_plugins(plugins, &PluginContext::new_empty()) - .await; - for (plugin_dir, error_msg) in errors { - eprintln!( - "Warning: Failed to initialize plugin '{}': {}", - plugin_dir, error_msg - ); - } - } - - Some(plugin_manager) } else { None }; diff --git a/crates-tauri/yaak-app/src/plugins_ext.rs b/crates-tauri/yaak-app/src/plugins_ext.rs index 6ca1e951..89172474 100644 --- a/crates-tauri/yaak-app/src/plugins_ext.rs +++ b/crates-tauri/yaak-app/src/plugins_ext.rs @@ -27,7 +27,8 @@ use yaak_plugins::api::{ PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse, check_plugin_updates, search_plugins, }; -use yaak_plugins::events::{Color, Icon, PluginContext, ShowToastRequest}; +use yaak_plugins::bootstrap; +use yaak_plugins::events::PluginContext; use yaak_plugins::install::{delete_and_uninstall, download_and_install}; use yaak_plugins::manager::PluginManager; use yaak_plugins::plugin_meta::get_plugin_meta; @@ -267,45 +268,23 @@ pub fn init() -> TauriPlugin { .join("index.cjs"); let dev_mode = is_dev(); + let query_manager = + app_handle.state::().inner().clone(); // Create plugin manager asynchronously let app_handle_clone = app_handle.clone(); tauri::async_runtime::block_on(async move { - let manager = PluginManager::new( + let manager = bootstrap::create_and_initialize_manager( vendored_plugin_dir, installed_plugin_dir, node_bin_path, plugin_runtime_main, + &query_manager, + &PluginContext::new_empty(), dev_mode, ) - .await; - - let db = app_handle_clone.db(); - manager - .ensure_bundled_plugins_registered(&db) - .await - .expect("Failed to register bundled plugins"); - - // Get all plugins from database and initialize - let plugins = db.list_plugins().expect("Failed to list plugins from database"); - drop(db); // Explicitly drop the connection before await - - let errors = - manager.initialize_all_plugins(plugins, &PluginContext::new_empty()).await; - - // Show toast for any failed plugins - for (plugin_dir, error_msg) in errors { - let plugin_name = plugin_dir.split('/').last().unwrap_or(&plugin_dir); - let toast = ShowToastRequest { - message: format!("Failed to start plugin '{}': {}", plugin_name, error_msg), - color: Some(Color::Danger), - icon: Some(Icon::AlertTriangle), - timeout: Some(10000), - }; - if let Err(emit_err) = app_handle_clone.emit("show_toast", toast) { - error!("Failed to emit toast for plugin error: {emit_err:?}"); - } - } + .await + .expect("Failed to initialize plugins"); app_handle_clone.manage(manager); }); diff --git a/crates/yaak-plugins/src/bootstrap.rs b/crates/yaak-plugins/src/bootstrap.rs new file mode 100644 index 00000000..797735d7 --- /dev/null +++ b/crates/yaak-plugins/src/bootstrap.rs @@ -0,0 +1,66 @@ +use crate::error::{Error, Result}; +use crate::events::PluginContext; +use crate::manager::PluginManager; +use std::path::PathBuf; +use std::sync::Arc; +use yaak_models::models::Plugin; +use yaak_models::query_manager::QueryManager; +use yaak_models::util::UpdateSource; + +/// Create a plugin manager and initialize all registered plugins. +/// +/// This performs: +/// 1. Plugin runtime startup (`PluginManager::new`) +/// 2. Bundled plugin registration in DB (if missing) +/// 3. Plugin initialization from DB +pub async fn create_and_initialize_manager( + vendored_plugin_dir: PathBuf, + installed_plugin_dir: PathBuf, + node_bin_path: PathBuf, + plugin_runtime_main: PathBuf, + query_manager: &QueryManager, + plugin_context: &PluginContext, + dev_mode: bool, +) -> Result> { + let plugin_manager = Arc::new( + PluginManager::new( + vendored_plugin_dir, + installed_plugin_dir, + node_bin_path, + plugin_runtime_main, + dev_mode, + ) + .await, + ); + + let bundled_dirs = plugin_manager.list_bundled_plugin_dirs().await?; + let db = query_manager.connect(); + for dir in bundled_dirs { + if db.get_plugin_by_directory(&dir).is_none() { + db.upsert_plugin( + &Plugin { + directory: dir, + enabled: true, + url: None, + ..Default::default() + }, + &UpdateSource::Background, + )?; + } + } + + let plugins = db.list_plugins()?; + drop(db); + + let init_errors = plugin_manager.initialize_all_plugins(plugins, plugin_context).await; + if !init_errors.is_empty() { + let joined = init_errors + .into_iter() + .map(|(dir, err)| format!("{dir}: {err}")) + .collect::>() + .join("; "); + return Err(Error::PluginErr(format!("Failed to initialize plugin(s): {joined}"))); + } + + Ok(plugin_manager) +} diff --git a/crates/yaak-plugins/src/lib.rs b/crates/yaak-plugins/src/lib.rs index 510756d0..b82414d1 100644 --- a/crates/yaak-plugins/src/lib.rs +++ b/crates/yaak-plugins/src/lib.rs @@ -7,6 +7,7 @@ //! by yaak-app's plugins_ext module. pub mod api; +pub mod bootstrap; mod checksum; pub mod error; pub mod events; diff --git a/crates/yaak-plugins/src/manager.rs b/crates/yaak-plugins/src/manager.rs index e1399c67..66c854d6 100644 --- a/crates/yaak-plugins/src/manager.rs +++ b/crates/yaak-plugins/src/manager.rs @@ -33,9 +33,8 @@ use tokio::net::TcpListener; use tokio::sync::mpsc::error::TrySendError; use tokio::sync::{Mutex, mpsc, oneshot}; use tokio::time::{Instant, timeout}; -use yaak_models::db_context::DbContext; use yaak_models::models::Plugin; -use yaak_models::util::{UpdateSource, generate_id}; +use yaak_models::util::generate_id; use yaak_templates::error::Error::RenderError; use yaak_templates::error::Result as TemplateResult; @@ -181,33 +180,6 @@ impl PluginManager { read_plugins_dir(&plugins_dir).await } - /// Ensure all bundled plugin directories are present in the plugins table. - /// Returns a list of newly registered plugin directories. - pub async fn ensure_bundled_plugins_registered( - &self, - db: &DbContext<'_>, - ) -> Result> { - let bundled_dirs = self.list_bundled_plugin_dirs().await?; - let mut registered = Vec::new(); - - for dir in bundled_dirs { - if db.get_plugin_by_directory(&dir).is_none() { - db.upsert_plugin( - &Plugin { - directory: dir.clone(), - enabled: true, - url: None, - ..Default::default() - }, - &UpdateSource::Background, - )?; - registered.push(dir); - } - } - - Ok(registered) - } - pub async fn uninstall(&self, plugin_context: &PluginContext, dir: &str) -> Result<()> { let plugin = self.get_plugin_by_dir(dir).await.ok_or(PluginNotFoundErr(dir.to_string()))?; self.remove_plugin(plugin_context, &plugin).await diff --git a/scripts/vendor-plugins.cjs b/scripts/vendor-plugins.cjs index 10c10eaf..6b900929 100644 --- a/scripts/vendor-plugins.cjs +++ b/scripts/vendor-plugins.cjs @@ -1,4 +1,4 @@ -const { readdirSync, cpSync, existsSync } = require('node:fs'); +const { readdirSync, cpSync, existsSync, mkdirSync } = require('node:fs'); const path = require('node:path'); const pluginsDir = path.join(__dirname, '..', 'plugins'); @@ -24,6 +24,7 @@ for (const name of readdirSync(pluginsDir)) { continue; } const destDir = path.join(__dirname, '../crates-tauri/yaak-app/vendored/plugins/', name); + mkdirSync(destDir, { recursive: true }); console.log(`Copying ${name} to ${destDir}`); cpSync(path.join(dir, 'package.json'), path.join(destDir, 'package.json')); cpSync(path.join(dir, 'build'), path.join(destDir, 'build'), { recursive: true });