Refactor: remove desktop/Tauri bundle, add i18n, CI/CD pipeline
Nightly Build / build (push) Failing after 6s
Nightly Build / build (push) Failing after 6s
- Remove desktop (Tauri) bundle: docs/desktop.md, icons/, tauri.conf.json, src/desktop/mod.rs, gen/schemas/ - Remove build.rs (no longer needed) - Add i18n system (crates/core-api, plugin-mobile-connector, web) - Refactor config system (src/config.rs, boot_format.rs) - Add mobile connector features (app, router, device pairing) - Plugin system improvements (skald-core) - Update dependencies (Cargo.lock, Cargo.toml) - CI/CD: Gitea Actions workflows (nightly + release), package.sh, verify-version.sh, builds.skaldagent.net config
This commit is contained in:
+1
-147
@@ -12,15 +12,6 @@ pub use skald_core::config::{
|
||||
const DEFAULT_CONFIG: &str = "default.config.yaml";
|
||||
const CONFIG: &str = "config.yml";
|
||||
|
||||
/// Default config baked into the binary at compile time.
|
||||
///
|
||||
/// Used by [`bootstrap_data_dir`] (desktop mode) to seed `config.yml` on first
|
||||
/// launch, where the bundled binary cannot rely on `default.config.yaml` being
|
||||
/// next to it on disk (the cwd has already been relocated to the per-user data
|
||||
/// dir). Headless mode still copies `default.config.yaml` from the source tree
|
||||
/// as before.
|
||||
const DEFAULT_CONFIG_EMBEDDED: &str = include_str!("../default.config.yaml");
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub server: ServerConfig,
|
||||
@@ -104,144 +95,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Absolute directory for log files.
|
||||
///
|
||||
/// `init_logging()` runs **before** [`bootstrap_data_dir`] relocates the cwd, so
|
||||
/// a relative `"logs"` path would resolve against the bundle's launch cwd (`/`
|
||||
/// for a Finder-launched `.app`) — un-writable, so the log folder stays empty.
|
||||
/// In a packaged bundle, return an absolute path under the per-user data dir
|
||||
/// instead. In every other mode (headless, desktop dev) keep the historical
|
||||
/// relative `"logs"`, unchanged.
|
||||
/// Directory for log files: a relative `"logs"` under the launch cwd.
|
||||
pub fn resolved_log_dir() -> std::path::PathBuf {
|
||||
#[cfg(feature = "desktop")]
|
||||
{
|
||||
if running_from_bundle() {
|
||||
if let Some(dir) = dirs::data_dir() {
|
||||
return dir.join("Skald").join("logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
std::path::PathBuf::from("logs")
|
||||
}
|
||||
|
||||
/// In desktop mode (Tauri bundle), relocate the process working directory to
|
||||
/// the OS-appropriate per-user data dir so that every relative path in
|
||||
/// `config.yml` (db, logs, data, secrets, models, agents, …) resolves there
|
||||
/// instead of `/` (the default cwd of a `.app` bundle on macOS, or the Windows
|
||||
/// equivalent). Also seeds `config.yml` from the bundled default if missing.
|
||||
///
|
||||
/// | OS | Location |
|
||||
/// |---------|-----------------------------------------------------|
|
||||
/// | macOS | `~/Library/Application Support/Skald` |
|
||||
/// | Windows | `%APPDATA%\Skald` (= `C:\Users\<u>\AppData\Roaming`)|
|
||||
/// | Linux | `~/.local/share/Skald` |
|
||||
///
|
||||
/// ## When relocation happens
|
||||
/// Only when the process is running from a packaged bundle (e.g. inside
|
||||
/// `Skald.app/Contents/MacOS/`). In dev mode (`cargo run --features desktop`),
|
||||
/// the cwd is left untouched so all source-tree assets (`agents/`, `skills/`,
|
||||
/// `web/`, `config.yml`, …) keep resolving from the crate root as in headless
|
||||
/// mode.
|
||||
///
|
||||
/// In headless mode this is always a no-op: the cwd stays as the user launched
|
||||
/// it, preserving today's behaviour (`./database.db`, `./logs/`, …).
|
||||
#[cfg(feature = "desktop")]
|
||||
pub fn bootstrap_data_dir() -> Result<()> {
|
||||
use tracing::info;
|
||||
if !running_from_bundle() {
|
||||
info!("desktop mode (dev): cwd unchanged — using source-tree assets");
|
||||
return Ok(());
|
||||
}
|
||||
let data_dir = dirs::data_dir()
|
||||
.context("could not determine OS data directory")?
|
||||
.join("Skald");
|
||||
std::fs::create_dir_all(&data_dir)
|
||||
.with_context(|| format!("failed to create data dir at {}", data_dir.display()))?;
|
||||
info!(path = %data_dir.display(), "desktop mode: relocating cwd to per-user data dir");
|
||||
std::env::set_current_dir(&data_dir)
|
||||
.with_context(|| format!("failed to cd to {}", data_dir.display()))?;
|
||||
|
||||
// Seed config.yml from the bundled default if absent (first launch).
|
||||
let config_path = Path::new(CONFIG);
|
||||
if !config_path.exists() {
|
||||
let _ = std::fs::write(CONFIG, DEFAULT_CONFIG_EMBEDDED);
|
||||
info!(path = %data_dir.display(), "seeded config.yml from embedded default");
|
||||
}
|
||||
|
||||
// Make the read-only bundled assets reachable from the relocated cwd.
|
||||
link_bundled_assets(&data_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read-only asset directories shipped inside the `.app` bundle's `Resources/`
|
||||
/// dir (see `tauri.conf.json > bundle > resources`). The backend looks these up
|
||||
/// by relative path from the cwd (agent discovery reads `agents/`, Axum serves
|
||||
/// `web/`, etc.), but the cwd has just been relocated to the data dir — where
|
||||
/// they don't exist. Without this, `Skald::new` fails with
|
||||
/// "Failed to read agents directory 'agents'" and the app exits on launch.
|
||||
#[cfg(feature = "desktop")]
|
||||
const BUNDLED_ASSETS: &[&str] = &["agents", "web", "skills", "commands"];
|
||||
|
||||
/// (Re)link each bundled asset dir into the per-user data dir as a symlink to
|
||||
/// the copy inside the app bundle's `Resources/`, so the existing relative-path
|
||||
/// lookups resolve while mutable state (db, config, logs, secrets) stays in the
|
||||
/// data dir itself.
|
||||
///
|
||||
/// Symlinking (rather than copying) keeps the assets in sync with the installed
|
||||
/// app version automatically. A pre-existing **real** directory is treated as a
|
||||
/// user override and left untouched; only symlinks are refreshed.
|
||||
#[cfg(feature = "desktop")]
|
||||
fn link_bundled_assets(data_dir: &Path) -> Result<()> {
|
||||
use tracing::{info, warn};
|
||||
let exe = std::env::current_exe().context("could not resolve current_exe")?;
|
||||
// `.../Skald.app/Contents/MacOS/skald` → `.../Skald.app/Contents/Resources`
|
||||
let resource_dir = match exe.parent().and_then(|p| p.parent()) {
|
||||
Some(contents) => contents.join("Resources"),
|
||||
None => {
|
||||
warn!("could not derive bundle Resources dir from exe path — skipping asset link");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
for name in BUNDLED_ASSETS {
|
||||
let src = resource_dir.join(name);
|
||||
if !src.exists() {
|
||||
warn!(asset = name, "bundled asset missing from Resources — skipping");
|
||||
continue;
|
||||
}
|
||||
let dst = data_dir.join(name);
|
||||
match std::fs::symlink_metadata(&dst) {
|
||||
// Stale symlink from a previous launch — replace it.
|
||||
Ok(meta) if meta.file_type().is_symlink() => { let _ = std::fs::remove_file(&dst); }
|
||||
// A real dir/file the user created — respect it, don't clobber.
|
||||
Ok(_) => continue,
|
||||
// Absent — fall through and create.
|
||||
Err(_) => {}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(&src, &dst)
|
||||
.with_context(|| format!("failed to symlink {} -> {}", dst.display(), src.display()))?;
|
||||
info!(asset = name, target = %src.display(), "linked bundled asset into data dir");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Heuristic: are we running inside a packaged bundle (e.g. `Foo.app`)?
|
||||
/// Used to decide whether to relocate the cwd to the per-user data dir.
|
||||
#[cfg(feature = "desktop")]
|
||||
fn running_from_bundle() -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
return exe.to_string_lossy().contains(".app/");
|
||||
}
|
||||
}
|
||||
// Windows / Linux: TBD when packaging targets land. For now treat all
|
||||
// launches as dev mode (no cwd relocation).
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
pub fn bootstrap_data_dir() -> Result<()> {
|
||||
// Headless mode: keep cwd as launched, no relocation.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user