chore: forcefully migrate old launcher path to new path (#895)

This commit is contained in:
LynithDev 2026-09-30 20:43:59 +02:00 • committed by GitHub
parent e1be4c8bcc
commit d630c27a88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 131 additions and 3 deletions

View file

@ -110,6 +110,8 @@ fn main() {
let rt = builder.build().unwrap();
let _tokio_guard = rt.enter();
let adopted = rt.block_on(oneclient_core::relocate::adopt_legacy_dir());
// no settings file is the sign of a fresh install, but not proof of one
let never_set_up = oneclient_common::paths::settings_file()
.map(|path| !path.exists())
@ -151,6 +153,14 @@ fn main() {
}
.expect("Failed to initialize logger");
match adopted {
Ok(Some(from)) => {
tracing::info!(from = %from.display(), "moved out of the old launcher folder")
}
Ok(None) => {}
Err(err) => tracing::error!("{err}"),
}
if let Some(reason) = unprotected {
tracing::warn!("no single-instance endpoint, a second launcher can start: {reason}");
}

View file

@ -14,7 +14,8 @@ const APPLICATION: &str = "OneClient";
#[cfg(debug_assertions)]
const APPLICATION: &str = "OneClient-dev";
const SETTINGS_FILE: &str = "settings.json";
pub const SETTINGS_FILE: &str = "settings.json";
pub const DATABASE_FILE: &str = "user_data.db";
static DEFAULT_DIR: OnceLock<PathBuf> = OnceLock::new();
static CONFIG_DIR_OVERRIDE: OnceLock<PathBuf> = OnceLock::new();
@ -35,7 +36,8 @@ fn organization_dir() -> PathsResult<PathBuf> {
.ok_or(PathsError::DataDirUnavailable)
}
fn legacy_dir() -> Option<PathBuf> {
#[must_use]
pub fn legacy_dir() -> Option<PathBuf> {
ProjectDirs::from(QUALIFIER, ORGANIZATION, APPLICATION)
.map(|dirs| dirs.data_local_dir().to_path_buf())
}
@ -47,6 +49,10 @@ fn resolve_default_dir() -> PathsResult<PathBuf> {
return Ok(legacy);
}
standard_dir()
}
pub fn standard_dir() -> PathsResult<PathBuf> {
Ok(organization_dir()?.join(APPLICATION))
}
@ -87,7 +93,7 @@ pub fn picker_start_dir() -> Option<PathBuf> {
}
pub fn database_file() -> PathsResult<PathBuf> {
Ok(data_dir()?.join("user_data.db"))
Ok(data_dir()?.join(DATABASE_FILE))
}
pub fn settings_file() -> PathsResult<PathBuf> {

View file

@ -285,6 +285,118 @@ pub fn incomplete(dir: &Path) -> bool {
dir.join(IN_PROGRESS_MARKER).exists()
}
pub async fn adopt_legacy_dir() -> Result<Option<PathBuf>, String> {
let (Some(from), Ok(to)) = (paths::legacy_dir(), paths::standard_dir()) else {
return Ok(None);
};
let Ok(raw) = polyio::read(from.join(paths::SETTINGS_FILE)).await else {
return Ok(None);
};
let chosen_folder = serde_json::from_slice::<serde_json::Value>(&raw)
.map_or(true, |settings| !settings["data_dir"].is_null());
if chosen_folder {
return Ok(None);
}
#[cfg(unix)]
if std::os::unix::net::UnixStream::connect(from.join("ipc.sock")).is_ok() {
return Ok(None);
}
let old = polyio::canonicalize(&from).unwrap_or_else(|_| from.clone());
if let Some(parent) = to.parent() {
polyio::create_dir_all(parent)
.await
.map_err(|err| format!("Couldn't create {}: {err}", parent.display()))?;
}
polyio::remove_dir(&to).await.ok();
polyio::rename(&from, &to).await.map_err(|err| {
format!(
"Couldn't move {} to {}, staying on the old folder: {err}",
from.display(),
to.display()
)
})?;
let database = to.join(paths::DATABASE_FILE);
if database.is_file()
&& let Err(err) = rewrite_java_paths(&database, &from, &to).await
{
return match polyio::rename(&to, &from).await {
Ok(()) => Err(format!("{err} Staying on {}.", from.display())),
Err(back) => Err(format!(
"{err} Moving {} back failed too: {back}",
to.display()
)),
};
}
for dir in from.ancestors().skip(1).take(2) {
if polyio::remove_dir(dir).await.is_err() {
break;
}
}
let new = polyio::canonicalize(&to).unwrap_or_else(|_| to.clone());
repoint_links(&new, &old, &from).await.map_err(|err| {
format!(
"Moved to {} but a link could not be repointed: {err}",
to.display()
)
})?;
Ok(Some(from))
}
async fn repoint_links(root: &Path, old: &Path, raw_old: &Path) -> Result<(), polyio::IOError> {
let mut stack = vec![root.to_path_buf()];
while let Some(dir) = stack.pop() {
let mut entries = polyio::read_dir(&dir).await?;
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
let Ok(kind) = entry.file_type().await else {
continue;
};
if kind.is_dir() {
stack.push(path);
continue;
}
if !kind.is_symlink() {
continue;
}
let Ok(target) = polyio::read_link(&path).await else {
continue;
};
let Ok(rest) = target
.strip_prefix(old)
.or_else(|_| target.strip_prefix(raw_old))
else {
continue;
};
let target = root.join(rest);
if target.is_dir() {
polyio::remove_symlink_dir(&path).await?;
polyio::symlink_dir(&target, &path).await?;
} else {
polyio::remove_file(&path).await?;
polyio::symlink_file(&target, &path).await?;
}
}
}
Ok(())
}
fn current_dir() -> Result<PathBuf, String> {
paths::data_dir()
.map(Path::to_path_buf)