Skip to content

Commit 181b54b

Browse files
authored
Merge pull request #2908 from wgqqqqq/wgq/fix-beta-data-migrator
fix(installer): remove obsolete bundled migrator requirement
2 parents c615753 + a00e29e commit 181b54b

12 files changed

Lines changed: 43 additions & 175 deletions

File tree

‎OpenBitFun-Installer/AGENTS.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ pnpm --dir OpenBitFun-Installer run type-check # fron
5050
cargo check --manifest-path OpenBitFun-Installer/src-tauri/Cargo.toml # Tauri/Rust changes
5151
```
5252

53+
For installer payload validation and the independent Data Migrator boundary, run:
54+
55+
```bash
56+
node --test OpenBitFun-Installer/scripts/build-installer.test.cjs scripts/data-migrator-tauri-build.test.mjs
57+
cargo test --manifest-path OpenBitFun-Installer/src-tauri/Cargo.toml --lib installer::commands::tests
58+
```
59+
5360
Run the full installer build only for packaging, payload, native bundling,
5461
install/uninstall flow, registry, shortcut, or extraction changes:
5562

‎OpenBitFun-Installer/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ Instead of relying on the generic NSIS wizard UI from Tauri's built-in bundler,
1111
- **Full control** — Custom installation logic, right-click context menu, PATH integration
1212
- **Cross-platform potential** — Same codebase can target Windows, macOS, and Linux
1313

14+
## Legacy data migration
15+
16+
Data Migrator is distributed separately and is not included in this installer.
17+
To import legacy data, download and run the [standalone Data Migrator](../src/apps/data-migrator/README.md) after closing both applications.
18+
1419
## Common tasks
1520

1621
Requires Node.js 22.12+ and pnpm 10.15.0, matching the workspace baseline.

‎OpenBitFun-Installer/scripts/build-installer.cjs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const STRICT_PAYLOAD_VALIDATION = !isDev;
2828
const MIN_APP_EXE_BYTES = 5 * 1024 * 1024;
2929
const REQUIRED_PAYLOAD_FILES = [
3030
"openbitfun-desktop.exe",
31-
"openbitfun-data-migrator.exe",
3231
"frontend/dist/index.html",
3332
"mobile-web/dist/index.html",
3433
"resources/ext-host/extension-host.js",

‎OpenBitFun-Installer/scripts/build-installer.test.cjs‎

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -103,36 +103,30 @@ test("all Installer validators share the required runtime file contract", () =>
103103
path.join(installerRoot, "src-tauri", "src", "installer", "commands.rs"),
104104
"utf8"
105105
);
106-
const installerModRs = fs.readFileSync(
107-
path.join(installerRoot, "src-tauri", "src", "installer", "mod.rs"),
108-
"utf8"
109-
);
106+
for (const source of [buildRs, commandsRs]) {
107+
const declaration = source.match(/const REQUIRED_PAYLOAD_FILES: \[&str; (\d+)\] = \[([\s\S]*?)\];/);
108+
assert.equal(Number(declaration[1]), REQUIRED_PAYLOAD_FILES.length);
109+
const files = [...declaration[2].matchAll(/"([^"]+)"|MAIN_APP_EXE/g)]
110+
.map((match) => match[1] || "openbitfun-desktop.exe");
111+
assert.deepEqual(files, REQUIRED_PAYLOAD_FILES);
112+
}
110113
for (const relativePath of REQUIRED_PAYLOAD_FILES) {
111114
assert.match(buildRs, new RegExp(escapeRegExp(relativePath)));
112115
if (relativePath === "openbitfun-desktop.exe") {
113116
assert.match(commandsRs, /MAIN_APP_EXE/);
114-
} else if (relativePath === "openbitfun-data-migrator.exe") {
115-
assert.match(installerModRs, new RegExp(escapeRegExp(relativePath)));
116-
assert.match(commandsRs, /DATA_MIGRATOR_EXE/);
117117
} else {
118118
assert.match(commandsRs, new RegExp(escapeRegExp(relativePath)));
119119
}
120120
}
121121
});
122122

123-
test("Data Migrator launch resolves only the registered installation", () => {
124-
const commandsRs = fs.readFileSync(
125-
path.join(installerRoot, "src-tauri", "src", "installer", "commands.rs"),
126-
"utf8"
127-
);
128-
const commandStart = commandsRs.indexOf("pub(crate) fn launch_legacy_data_migrator");
129-
const commandEnd = commandsRs.indexOf("/// Close the installer window.", commandStart);
130-
assert.notEqual(commandStart, -1);
131-
assert.notEqual(commandEnd, -1);
132-
const commandSource = commandsRs.slice(commandStart, commandEnd);
133-
assert.doesNotMatch(commandSource, /request\.install_path/);
134-
assert.match(commandSource, /read_existing_install_from_uninstall_registry/);
135-
assert.match(commandSource, /read_tauri_install_location/);
123+
test("standalone Data Migrator is not required by the installer", () => {
124+
assert.ok(!REQUIRED_PAYLOAD_FILES.includes("openbitfun-data-migrator.exe"));
125+
const commandsRs = fs.readFileSync(path.join(installerRoot, "src-tauri/src/installer/commands.rs"), "utf8");
126+
assert.doesNotMatch(commandsRs, /DATA_MIGRATOR_EXE|HandoffStore|launch_trusted_executable/);
127+
assert.match(commandsRs, /Data Migrator is distributed separately/);
128+
const themeSetup = fs.readFileSync(path.join(installerRoot, "src/pages/ThemeSetup.tsx"), "utf8");
129+
assert.doesNotMatch(themeSetup, /migrateLegacyData|onLaunchMigration/);
136130
});
137131

138132
function minorLine(version) {

‎OpenBitFun-Installer/src-tauri/Cargo.toml‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ zip = "0.6"
3030
openbitfun-core-types = { path = "../../src/crates/contracts/core-types" }
3131
openbitfun-ai-adapters = { path = "../../src/crates/adapters/ai-adapters" }
3232
openbitfun-services-core = { path = "../../src/crates/services/services-core", features = ["json-io"] }
33-
openbitfun-legacy-migration = { path = "../../src/crates/services/legacy-migration" }
34-
openbitfun-product-domains = { path = "../../src/crates/contracts/product-domains", features = ["legacy-migration"] }
35-
uuid = { version = "1", features = ["v4"] }
3633

3734
[target.'cfg(windows)'.dependencies]
3835
winreg = "0.52"

‎OpenBitFun-Installer/src-tauri/build.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ use std::path::{Path, PathBuf};
55
use zip::write::FileOptions;
66
use zip::{CompressionMethod, ZipWriter};
77

8-
const REQUIRED_PAYLOAD_FILES: [&str; 7] = [
8+
const REQUIRED_PAYLOAD_FILES: [&str; 6] = [
99
"openbitfun-desktop.exe",
10-
"openbitfun-data-migrator.exe",
1110
"frontend/dist/index.html",
1211
"mobile-web/dist/index.html",
1312
"resources/ext-host/extension-host.js",

‎OpenBitFun-Installer/src-tauri/src/installer/commands.rs‎

Lines changed: 13 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,12 @@ use super::types::{
66
ConnectionTestResult, DiskSpaceInfo, InstallOptions, InstallProgress, ModelConfig,
77
RemoteModelInfo,
88
};
9-
use super::{DATA_MIGRATOR_EXE, MAIN_APP_EXE};
9+
use super::MAIN_APP_EXE;
1010
use openbitfun_core_types::{
1111
installer_config_handoff::{
1212
InstallerConfigHandoff, InstallerModelHandoff, INSTALLER_CONFIG_HANDOFF_FILE_NAME,
1313
},
14-
product_identity::{data_namespace, hidden_data_directory, product_id},
15-
};
16-
#[cfg(target_os = "windows")]
17-
use openbitfun_legacy_migration::{
18-
launch_trusted_executable, probe_legacy_source, HandoffStore, MigrationOnboardingStore,
19-
MigrationRoots, ProbeLimits, TrustedInstallationResolver,
20-
};
21-
#[cfg(target_os = "windows")]
22-
use openbitfun_product_domains::legacy_migration::{
23-
MigrationPromptChoice, MigrationSelection, MigratorHandoffRequest,
24-
MigratorProtocolCapabilities, MigratorRequestMode, MigratorRequestOrigin,
25-
CURRENT_MIGRATION_FORMAT_VERSION,
14+
product_identity::{data_namespace, hidden_data_directory},
2615
};
2716
use openbitfun_services_core::json_store::JsonFileStore;
2817
use serde::{Deserialize, Serialize};
@@ -33,8 +22,6 @@ use std::fs::File;
3322
use std::io::{Cursor, Read};
3423
use std::path::{Path, PathBuf};
3524
use std::sync::LazyLock;
36-
#[cfg(target_os = "windows")]
37-
use std::time::{SystemTime, UNIX_EPOCH};
3825
use tauri::{Emitter, Manager, Window};
3926

4027
#[cfg(target_os = "windows")]
@@ -48,23 +35,15 @@ struct WindowsInstallState {
4835

4936
const MIN_WINDOWS_APP_EXE_BYTES: u64 = 5 * 1024 * 1024;
5037
const PAYLOAD_MANIFEST_FILE: &str = "payload-manifest.json";
51-
const REQUIRED_PAYLOAD_FILES: [&str; 7] = [
38+
const REQUIRED_PAYLOAD_FILES: [&str; 6] = [
5239
MAIN_APP_EXE,
53-
DATA_MIGRATOR_EXE,
5440
"frontend/dist/index.html",
5541
"mobile-web/dist/index.html",
5642
"resources/ext-host/extension-host.js",
5743
"resources/worker_host.js",
5844
"flashgrep/flashgrep-x86_64-pc-windows-msvc.exe",
5945
];
6046
const INSTALLER_STATE_FILE: &str = "installer-state.json";
61-
#[cfg(target_os = "windows")]
62-
const MIGRATOR_HANDOFF_LIFETIME_MS: i64 = 10 * 60 * 1000;
63-
#[cfg(target_os = "windows")]
64-
const RELEASE_CHANNEL: &str = match option_env!("OPENBITFUN_RELEASE_CHANNEL") {
65-
Some(value) => value,
66-
None => "stable",
67-
};
6847
const EMBEDDED_PAYLOAD_ZIP: &[u8] =
6948
include_bytes!(concat!(env!("OUT_DIR"), "/embedded_payload.zip"));
7049

@@ -858,9 +837,7 @@ pub(crate) fn launch_application(install_path: String) -> Result<(), String> {
858837
Ok(())
859838
}
860839

861-
/// Create an Installer-origin onboarding handoff and launch the installed
862-
/// standalone Data Migrator. The Installer never reads or writes migrated
863-
/// product data itself.
840+
/// Retain the old command as an explicit unsupported response for older clients.
864841
#[derive(Debug, Default, Deserialize)]
865842
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
866843
pub(crate) struct LaunchLegacyDataMigratorRequest {}
@@ -869,98 +846,8 @@ pub(crate) struct LaunchLegacyDataMigratorRequest {}
869846
pub(crate) fn launch_legacy_data_migrator(
870847
request: LaunchLegacyDataMigratorRequest,
871848
) -> Result<bool, String> {
872-
#[cfg(not(target_os = "windows"))]
873-
{
874-
let _ = request;
875-
return Err(
876-
"Legacy data migration is not supported by this Installer platform.".to_string(),
877-
);
878-
}
879-
880-
#[cfg(target_os = "windows")]
881-
{
882-
let _ = request;
883-
let roots = MigrationRoots::resolve_current_user()
884-
.map_err(|_| "Could not resolve the current-user migration storage.".to_string())?;
885-
let Some(source) = probe_legacy_source(&roots, ProbeLimits::default())
886-
.map_err(|_| "Could not safely inspect older data.".to_string())?
887-
else {
888-
return Ok(false);
889-
};
890-
if !source.supported {
891-
return Err(
892-
"The discovered legacy data format is not supported by this Data Migrator."
893-
.to_string(),
894-
);
895-
}
896-
897-
let now_ms = migration_now_ms();
898-
let capabilities = MigratorProtocolCapabilities::current();
899-
let handoff = MigratorHandoffRequest {
900-
protocol_version: capabilities.protocol_version,
901-
mode: MigratorRequestMode::Onboarding,
902-
origin: MigratorRequestOrigin::Installer,
903-
run_id: uuid::Uuid::new_v4().to_string(),
904-
nonce: uuid::Uuid::new_v4().to_string(),
905-
source_id: Some(source.source_id.clone()),
906-
source_fingerprint: Some(source.source_fingerprint.clone()),
907-
selection: MigrationSelection::all(),
908-
caller_process_id: std::process::id(),
909-
product_id: product_id().to_string(),
910-
release_channel: RELEASE_CHANNEL.to_string(),
911-
created_at_ms: now_ms,
912-
expires_at_ms: now_ms.saturating_add(MIGRATOR_HANDOFF_LIFETIME_MS),
913-
required_capabilities: capabilities.capabilities,
914-
};
915-
HandoffStore::new(roots.clone(), product_id(), RELEASE_CHANNEL)
916-
.write_request(&handoff, now_ms)
917-
.map_err(|_| "Could not create a safe Data Migrator handoff.".to_string())?;
918-
MigrationOnboardingStore::new(roots)
919-
.update(|state| {
920-
state.format_version = CURRENT_MIGRATION_FORMAT_VERSION;
921-
state.source_fingerprint = source.source_fingerprint.clone();
922-
state.detected_at_ms.get_or_insert(now_ms);
923-
state.choice = MigrationPromptChoice::Unset;
924-
state.last_prompted_version = Some(env!("CARGO_PKG_VERSION").to_string());
925-
state.run_id = Some(handoff.run_id.clone());
926-
state.handled_run_id = None;
927-
})
928-
.map_err(|_| "Could not persist the Data Migrator handoff state.".to_string())?;
929-
930-
let registered_install_path = super::registry::read_existing_install_from_uninstall_registry()
931-
.map(|registration| registration.install_location)
932-
.or_else(super::registry::read_tauri_install_location)
933-
.ok_or_else(|| {
934-
"The registered OpenBitFun installation could not be found. Repair the installation before starting Data Migrator."
935-
.to_string()
936-
})?;
937-
let desktop = PathBuf::from(registered_install_path).join(MAIN_APP_EXE);
938-
let executable = TrustedInstallationResolver::resolve_sibling(
939-
&desktop,
940-
MAIN_APP_EXE,
941-
DATA_MIGRATOR_EXE,
942-
)
943-
.map_err(|_| {
944-
"The installed Data Migrator is missing or failed installation layout checks. Repair the OpenBitFun installation."
945-
.to_string()
946-
})?;
947-
launch_trusted_executable(
948-
&executable,
949-
&[std::ffi::OsStr::new(handoff.run_id.as_str())],
950-
)
951-
.map_err(|_| "Could not launch the installed Data Migrator.".to_string())?;
952-
Ok(true)
953-
}
954-
}
955-
956-
#[cfg(target_os = "windows")]
957-
fn migration_now_ms() -> i64 {
958-
SystemTime::now()
959-
.duration_since(UNIX_EPOCH)
960-
.unwrap_or_default()
961-
.as_millis()
962-
.try_into()
963-
.unwrap_or(i64::MAX)
849+
let _ = request;
850+
Err("Data Migrator is distributed separately. Download and run OpenBitFun Data Migrator to import legacy data.".to_string())
964851
}
965852

966853
/// Close the installer window.
@@ -2005,6 +1892,13 @@ mod tests {
20051892
use std::io::{Cursor, Write};
20061893
use zip::write::FileOptions;
20071894

1895+
#[test]
1896+
fn legacy_migration_command_reports_the_independent_distribution() {
1897+
let result =
1898+
super::launch_legacy_data_migrator(super::LaunchLegacyDataMigratorRequest::default());
1899+
assert!(result.unwrap_err().contains("distributed separately"));
1900+
}
1901+
20081902
#[test]
20091903
fn language_alias_priority_is_descending_and_stable_for_equal_lengths() {
20101904
let aliases = INSTALLER_APP_LANGUAGE_ALIASES_BY_PRIORITY.as_slice();

‎OpenBitFun-Installer/src-tauri/src/installer/mod.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ mod types;
66

77
/// Windows main binary file name — must match `src/apps/desktop` `[[bin]]` and Tauri NSIS output.
88
const MAIN_APP_EXE: &str = "openbitfun-desktop.exe";
9-
const DATA_MIGRATOR_EXE: &str = "openbitfun-data-migrator.exe";
109

1110
#[cfg(target_os = "windows")]
1211
mod registry;

‎OpenBitFun-Installer/src/App.tsx‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ function App() {
9090
options={installer.options}
9191
setOptions={installer.setOptions}
9292
onLaunch={installer.launchApp}
93-
onLaunchMigration={installer.launchLegacyDataMigrator}
9493
onClose={installer.closeInstaller}
9594
/>
9695
);

‎OpenBitFun-Installer/src/hooks/useInstaller.ts‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export interface UseInstallerReturn {
3838
saveModelConfig: () => Promise<void>;
3939
testModelConnection: (modelConfig: ModelConfig) => Promise<ConnectionTestResult>;
4040
launchApp: () => Promise<void>;
41-
launchLegacyDataMigrator: () => Promise<boolean>;
4241
closeInstaller: () => void;
4342
refreshDiskSpace: (path: string) => Promise<void>;
4443
clearInstallError: () => void;
@@ -334,12 +333,6 @@ export function useInstaller(): UseInstallerReturn {
334333
await invoke('launch_application', { installPath: options.installPath });
335334
}, [options.installPath]);
336335

337-
const launchLegacyDataMigrator = useCallback(async () => {
338-
return invoke<boolean>('launch_legacy_data_migrator', {
339-
request: {},
340-
});
341-
}, []);
342-
343336
const closeInstaller = useCallback(() => {
344337
invoke('close_installer');
345338
}, []);
@@ -386,7 +379,7 @@ export function useInstaller(): UseInstallerReturn {
386379
progress, isInstalling, installationCompleted, error, diskSpace,
387380
existingInstall, launchRegisteredUninstaller,
388381
install, canConfirmProgress, confirmProgress, retryInstall, backToOptions,
389-
saveModelConfig, testModelConnection, launchApp, launchLegacyDataMigrator, closeInstaller, refreshDiskSpace, clearInstallError,
382+
saveModelConfig, testModelConnection, launchApp, closeInstaller, refreshDiskSpace, clearInstallError,
390383
isUninstallMode, isUninstalling, uninstallCompleted, uninstallError, uninstallProgress, startUninstall,
391384
};
392385
}

0 commit comments

Comments
 (0)