Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
103 changes: 102 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,110 @@ jobs:
path: apps/omninova-ios/release-assets/**
if-no-files-found: error

cli:
name: CLI ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- name: Linux
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
asset_platform: linux-x64
bin: omninova
ext: ""
- name: macOS (Intel)
os: macos-latest
target: x86_64-apple-darwin
asset_platform: macos-intel
bin: omninova
ext: ""
- name: macOS (Apple Silicon)
os: macos-14
target: aarch64-apple-darwin
asset_platform: macos-arm64
bin: omninova
ext: ""
- name: Windows
os: windows-latest
target: x86_64-pc-windows-msvc
asset_platform: windows-x64
bin: omninova.exe
ext: ".exe"

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup MSVC
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1

- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: . -> target

- name: Install Linux system dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config build-essential libssl-dev

- name: Prepare CLI version
id: meta
shell: bash
run: |
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
VERSION="${GITHUB_REF_NAME#v}"
else
BRANCH_NAME="${GITHUB_REF_NAME:-branch}"
SANITIZED_BRANCH=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | sed 's#[^a-z0-9._-]#-#g')
VERSION="${SANITIZED_BRANCH}-sha-${GITHUB_SHA::7}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Build omninova CLI
shell: bash
run: cargo build --release -p omninova-core --bin omninova --target ${{ matrix.target }}

- name: Stage CLI artifact
shell: bash
run: |
set -euo pipefail
mkdir -p cli-assets
SRC="target/${{ matrix.target }}/release/${{ matrix.bin }}"
if [ ! -f "$SRC" ]; then
echo "::error::CLI binary not found at $SRC"
ls -R target/${{ matrix.target }}/release | head -50 || true
exit 1
fi
DEST="cli-assets/omninova-${{ steps.meta.outputs.version }}-${{ matrix.asset_platform }}${{ matrix.ext }}"
cp "$SRC" "$DEST"
ls -lh cli-assets/

- name: Upload CLI artifact
uses: actions/upload-artifact@v4
with:
name: release-cli-${{ matrix.asset_platform }}
path: cli-assets/**
if-no-files-found: error

release:
name: Publish GitHub Release
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs:
- desktop
- android
- ios
- cli
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
Expand All @@ -530,7 +627,7 @@ jobs:
find . -mindepth 2 -name "SHA256SUMS.txt" -type f -delete
mapfile -t release_files < <(
find . -type f \
\( -name "*.dmg" -o -name "*.deb" -o -name "*.appimage" -o -name "*.rpm" -o -name "*.exe" -o -name "*.msi" -o -name "*.tar.gz" -o -name "*.apk" -o -name "*.aab" -o -name "*.ipa" \) \
\( -name "*.dmg" -o -name "*.deb" -o -name "*.appimage" -o -name "*.rpm" -o -name "*.exe" -o -name "*.msi" -o -name "*.tar.gz" -o -name "*.apk" -o -name "*.aab" -o -name "*.ipa" -o -name "omninova-*-linux-x64" -o -name "omninova-*-macos-intel" -o -name "omninova-*-macos-arm64" \) \
-print | sort
)
if [ "${#release_files[@]}" -eq 0 ]; then
Expand Down Expand Up @@ -560,6 +657,10 @@ jobs:
release-assets/**/*.apk
release-assets/**/*.aab
release-assets/**/*.ipa
release-assets/**/omninova-*-linux-x64
release-assets/**/omninova-*-macos-intel
release-assets/**/omninova-*-macos-arm64
release-assets/**/omninova-*-windows-x64.exe
release-assets/**/IOS_INSTALL.txt
release-assets/**/macOS-安装与修复.txt
release-assets/SHA256SUMS.txt
15 changes: 15 additions & 0 deletions apps/omninova-tauri/src-tauri/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
fn main() {
tauri_build::build();

// Windows: raise the executable's default thread stack reserve to 8 MiB.
//
// The `Config` struct is large and deeply nested, so serde (de)serialization
// to/from JSON/TOML is stack-hungry. Tauri deserializes command arguments and
// serializes results on its IPC/main threads — created with the OS default
// stack size (PE `SizeOfStackReserve`, ~1 MiB by default on MSVC). That caused
// a `0xC00000FD` (STATUS_STACK_OVERFLOW) crash when saving the model config.
// Threads created with stack size 0 inherit this reserve, so this covers the
// main thread, Tauri/wry internal threads and the IPC response path.
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if target_os == "windows" && target_env == "msvc" {
println!("cargo:rustc-link-arg-bins=/STACK:8388608");
}

let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let workspace_root = manifest_dir.join("../../..");
let profile = std::env::var("PROFILE").unwrap_or_else(|_| "debug".into());
Expand Down
35 changes: 28 additions & 7 deletions apps/omninova-tauri/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,7 @@ async fn save_setup_config(
let mut next = setup_config_to_core(current, config)?;
let next_gateway_url = format!("http://{}:{}", next.gateway.host, next.gateway.port);

if let Err(error) = save_config_with_fallback(&mut next) {
eprintln!("[config warning] {error}");
}
save_config_with_fallback(&mut next)?;
runtime.set_config(next).await.map_err(|e| e.to_string())?;

if current_gateway_url != next_gateway_url {
Expand Down Expand Up @@ -1177,15 +1175,19 @@ fn normalize_optional_string(value: Option<String>) -> Option<String> {
})
}

fn user_home_dir() -> Option<PathBuf> {
std::env::var_os("HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("USERPROFILE").map(PathBuf::from))
}

fn expand_tilde_path(value: &str) -> PathBuf {
if value == "~" {
return std::env::var_os("HOME")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(value));
return user_home_dir().unwrap_or_else(|| PathBuf::from(value));
}

if let Some(rest) = value.strip_prefix("~/") {
if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) {
if let Some(home) = user_home_dir() {
return home.join(rest);
}
}
Expand Down Expand Up @@ -1249,8 +1251,27 @@ fn display_provider_name(id: &str) -> String {
}
}

/// Worker-thread stack size for the async runtime that backs Tauri commands.
///
/// The `Config` struct is large (~8.7 KiB) and very deeply nested, so serde
/// (de)serialization to/from TOML/JSON consumes a lot of stack. On Windows the
/// default worker-thread stack (~1 MiB) overflows during config save, crashing
/// the process with `0xC00000FD` (STATUS_STACK_OVERFLOW). An 8 MiB stack keeps
/// every command handler (which may (de)serialize `Config`) well within bounds.
const ASYNC_RUNTIME_STACK_BYTES: usize = 8 * 1024 * 1024;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// Install a tokio runtime with larger worker stacks *before* anything uses
// the async runtime, so all Tauri command handlers run with enough stack.
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(ASYNC_RUNTIME_STACK_BYTES)
.build()
.expect("Failed to build async runtime");
let runtime: &'static tokio::runtime::Runtime = Box::leak(Box::new(runtime));
tauri::async_runtime::set(runtime.handle().clone());

omninova_core::init().expect("Failed to initialize core");

let config = Config::load_or_init().expect("Failed to load config");
Expand Down
Loading