Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use self-replace to update maa-cli self #363

Merged
merged 4 commits into from
Jan 1, 2025
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ clap = "4.4"
clap_complete = "4.4"
clap_mangen = "0.2.20"
color-print = "0.3.6"
constcat = "0.5.1"
digest = "0.10.7"
directories = "5"
dunce = "1.0.4"
Expand All @@ -31,6 +32,7 @@ libloading = "0.8"
log = "0.4.20"
prettytable = { version = "0.10.0", default-features = false }
regex = "1.10.2"
self-replace = "1.5.0"
semver = "1.0.19"
serde = "1"
serde_json = "1"
Expand All @@ -39,6 +41,7 @@ serde_yaml = "0.9.25"
sha2 = "0.10.7"
signal-hook = "0.3.17"
tar = "0.4.40"
tempfile = "3.14.0"
thiserror = "2"
tokio = "1.31"
toml = "0.8"
Expand Down
3 changes: 3 additions & 0 deletions crates/maa-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ clap = { workspace = true, features = ["derive"] }
clap_complete = { workspace = true }
clap_mangen = { workspace = true }
color-print = { workspace = true }
constcat = { workspace = true }
digest = { workspace = true, optional = true }
dunce = { workspace = true }
env_logger = { workspace = true, features = ["auto-color"] }
Expand All @@ -69,6 +70,8 @@ tar = { workspace = true, optional = true }
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
toml = { workspace = true }
zip = { workspace = true, optional = true, features = ["deflate"] }
self-replace = { workspace = true }
tempfile = { workspace = true }

# Windows specific dependencies
[target.'cfg(windows)'.dependencies]
Expand Down
66 changes: 37 additions & 29 deletions crates/maa-cli/src/installer/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@
/// If the output path exists, the file will be skipped if the file size matches.
/// Otherwise, the file will be overwritten.
/// The file permissions will be preserved.
pub fn extract(&self, mapper: impl Fn(&Path) -> Option<PathBuf>) -> Result<()> {
pub fn extract<F>(&self, mapper: F) -> Result<()>

Check warning on line 72 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L72

Added line #L72 was not covered by tests
where
F: FnMut(Cow<Path>) -> Option<PathBuf>,
{
println!("Extracting archive file...");
match self.archive_type {
ArchiveType::Zip => extract_zip(&self.file, mapper),
Expand All @@ -78,24 +81,29 @@
}
}

fn extract_zip(file: &Path, mapper: impl Fn(&Path) -> Option<PathBuf>) -> Result<()> {
fn extract_zip<F>(file: &Path, mut mapper: F) -> Result<()>

Check warning on line 84 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L84

Added line #L84 was not covered by tests
where
F: FnMut(Cow<Path>) -> Option<PathBuf>,
{
let mut archive = zip::ZipArchive::new(File::open(file)?)?;

for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();

let outpath = match file.enclosed_name() {
Some(path) => match mapper(&path) {
Some(path) => path,
None => continue,
},
let src_path = file

Check warning on line 93 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L93

Added line #L93 was not covered by tests
.enclosed_name()
.context("Bad file path in zip archive")?
.into();
let dst = match mapper(src_path) {
Some(path) => path,

Check warning on line 98 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L97-L98

Added lines #L97 - L98 were not covered by tests
None => continue,
};
let dst = dst.as_path();

Check warning on line 101 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L101

Added line #L101 was not covered by tests

if file.is_dir() {
continue;
} else {
if let Some(p) = outpath.parent() {
if let Some(p) = dst.parent() {

Check warning on line 106 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L106

Added line #L106 was not covered by tests
p.ensure()?;
}

Expand All @@ -114,23 +122,23 @@
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
let link_target = std::ffi::OsString::from_vec(contents);
if outpath.exists() {
remove_file(&outpath).with_context(|| {
format!("Failed to remove existing file: {}", outpath.display())
if dst.exists() {
remove_file(dst).with_context(|| {
format!("Failed to remove existing file: {}", dst.display())

Check warning on line 127 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L125-L127

Added lines #L125 - L127 were not covered by tests
})?;
}
symlink(link_target, &outpath).with_context(|| {
format!("Failed to extract file: {}", outpath.display())
symlink(link_target, dst).with_context(|| {
format!("Failed to extract file: {}", dst.display())

Check warning on line 131 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L130-L131

Added lines #L130 - L131 were not covered by tests
})?;
continue;
}
}
}

let mut outfile = File::create(&outpath)
.with_context(|| format!("Failed to create file: {}", outpath.display()))?;
let mut outfile = File::create(dst)
.with_context(|| format!("Failed to create file: {}", dst.display()))?;

Check warning on line 139 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L138-L139

Added lines #L138 - L139 were not covered by tests
copy(&mut file, &mut outfile)
.with_context(|| format!("Failed to extract file: {}", outpath.display()))?;
.with_context(|| format!("Failed to extract file: {}", dst.display()))?;

Check warning on line 141 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L141

Added line #L141 was not covered by tests
}

#[cfg(unix)]
Expand All @@ -141,35 +149,35 @@
};

if let Some(mode) = file.unix_mode() {
set_permissions(&outpath, Permissions::from_mode(mode))
.with_context(|| format!("Failed to set permissions: {}", outpath.display()))?;
set_permissions(dst, Permissions::from_mode(mode))
.with_context(|| format!("Failed to set permissions: {}", dst.display()))?;

Check warning on line 153 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L152-L153

Added lines #L152 - L153 were not covered by tests
}
}
}

Ok(())
}

fn extract_tar_gz(file: &Path, mapper: impl Fn(&Path) -> Option<PathBuf>) -> Result<()> {
fn extract_tar_gz<F>(file: &Path, mut mapper: F) -> Result<()>

Check warning on line 161 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L161

Added line #L161 was not covered by tests
where
F: FnMut(Cow<Path>) -> Option<PathBuf>,
{
let gz_decoder = flate2::read::GzDecoder::new(File::open(file)?);
let mut archive = tar::Archive::new(gz_decoder);

for entry in archive.entries()? {
let mut file = entry?;

let outpath = match &file.path() {
Ok(path) => match mapper(path) {
Some(path) => path,
None => continue,
},
Err(e) => return Err(anyhow!("Error while reading tar entry: {}", e)),
let mut entry = entry?;
let entry_path = entry.path().context("Bad file path in tar.gz archive")?;
let dst = match mapper(entry_path) {
Some(path) => path,
None => continue,

Check warning on line 173 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L169-L173

Added lines #L169 - L173 were not covered by tests
};

if let Some(p) = outpath.parent() {
if let Some(p) = dst.parent() {

Check warning on line 176 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L176

Added line #L176 was not covered by tests
p.ensure()?;
}

file.unpack(&outpath)?;
entry.unpack(&dst)?;

Check warning on line 180 in crates/maa-cli/src/installer/extract.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/extract.rs#L180

Added line #L180 was not covered by tests
}

println!("Done!");
Expand Down
21 changes: 11 additions & 10 deletions crates/maa-cli/src/installer/maa_cli.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use std::{
collections::BTreeMap,
env::{consts, current_exe},
time::Duration,
};
use std::{collections::BTreeMap, time::Duration};

use anyhow::{anyhow, Context, Result};
use dunce::canonicalize;
use semver::Version;
use serde::Deserialize;
use tokio::runtime::Runtime;
Expand All @@ -20,6 +15,9 @@
dirs::{self, Ensure},
};

const MAA_CLI_NAME: &str = "maa";
const MAA_CLI_EXE: &str = constcat::concat!(MAA_CLI_NAME, std::env::consts::EXE_SUFFIX);

pub fn update(args: &CommonArgs) -> Result<()> {
let config = CLI_CONFIG.cli_config().with_args(args);

Expand All @@ -33,7 +31,6 @@
return Ok(());
}

let bin_path = canonicalize(current_exe()?)?;
let details = version_json.details();
let asset = details.asset()?;
let asset_name = asset.name();
Expand Down Expand Up @@ -61,15 +58,19 @@
.context("Failed to download maa-cli")?;
};

let cli_exe = format!("maa{}", consts::EXE_SUFFIX);
let tmp_dir = tempfile::tempdir()?;
let tmp_exe = tmp_dir.path().join(MAA_CLI_EXE);

Check warning on line 62 in crates/maa-cli/src/installer/maa_cli.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/maa_cli.rs#L61-L62

Added lines #L61 - L62 were not covered by tests

Archive::new(cache_path.into())?.extract(|path| {
if config.components().binary && path.ends_with(&cli_exe) {
Some(bin_path.clone())
if config.components().binary && path.ends_with(MAA_CLI_EXE) {
Some(tmp_exe.clone())

Check warning on line 66 in crates/maa-cli/src/installer/maa_cli.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/maa_cli.rs#L65-L66

Added lines #L65 - L66 were not covered by tests
} else {
None
}
})?;

self_replace::self_replace(tmp_exe)?;

Check warning on line 72 in crates/maa-cli/src/installer/maa_cli.rs

View check run for this annotation

Codecov / codecov/patch

crates/maa-cli/src/installer/maa_cli.rs#L72

Added line #L72 was not covered by tests

Ok(())
}

Expand Down
Loading
Loading