chore: forcefully migrate old launcher path to new path (#895)
This commit is contained in:
parent
e1be4c8bcc
commit
d630c27a88
3 changed files with 131 additions and 3 deletions
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue