Add Explorer++ and Minecraft installers and implement ExtractFile and ExecuteCommand

This commit is contained in:
Michael Pfaff 2022-06-09 18:29:58 -04:00
parent 35a3451061
commit d962fc5870
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
4 changed files with 120 additions and 27 deletions

10
Cargo.lock generated
View File

@ -874,6 +874,15 @@ dependencies = [
"serde",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.6"
@ -1017,6 +1026,7 @@ dependencies = [
"mio",
"once_cell",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",

View File

@ -11,6 +11,6 @@ ramhorns = "0.14"
reqwest = { version = "0.11.10", features = [ "json" ] }
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
tokio = { version = "1.19", features = [ "fs", "macros", "rt" ] }
tokio = { version = "1.19", features = [ "fs", "macros", "process", "rt" ] }
url = "2.2"

View File

@ -16,7 +16,10 @@ pub struct Pipeline<'a> {
impl<'a> Pipeline<'a> {
#[inline(always)]
pub fn new(name: &'a str, steps: impl Into<Vec<Step<'a>>>) -> Self {
Self { name, steps: steps.into() }
Self {
name,
steps: steps.into(),
}
}
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
@ -69,7 +72,10 @@ impl<'a> Step<'a> {
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
match self {
Self::DownloadFile { res, file } => {
println!("Downloading file {file} from {res:?}...", file = file.to_str().unwrap_or("<NON UTF-8>"));
println!(
"Downloading file {file} from {res:?}...",
file = file.to_str().unwrap_or("<NON UTF-8>")
);
const FETCH_FILE_ERROR_MSG: &'static str = "Fetching the remote resource failed.";
const WRITE_FILE_ERROR_MSG: &'static str =
"Writing the remote resource to disk failed.";
@ -85,7 +91,9 @@ impl<'a> Step<'a> {
))
.into_diagnostic()
.wrap_err("Invalid GitHub repo for download step.")?;
let mut release: GitHubRelease = ctx.reqwest.get(url)
let mut release: GitHubRelease = ctx
.reqwest
.get(url)
.send()
.await
.into_diagnostic()
@ -111,15 +119,22 @@ impl<'a> Step<'a> {
)?
}
};
let mut resp = ctx.reqwest.get(url)
let mut resp = ctx
.reqwest
.get(url)
.send()
.await
.into_diagnostic()
.wrap_err(FETCH_FILE_ERROR_MSG)?;
let _content_length = resp.content_length();
mkdir_all(file.parent().ok_or_else(|| miette!("Destination file for download step has no parent."))?).await?;
mkdir_all(
file.parent().ok_or_else(|| {
miette!("Destination file for download step has no parent.")
})?,
)
.await?;
let mut writer = tokio::io::BufWriter::new(
tokio::fs::File::create(file)
tokio::fs::File::create(file.as_os_str())
.await
.into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?,
@ -143,14 +158,43 @@ impl<'a> Step<'a> {
.wrap_err(WRITE_FILE_ERROR_MSG)?;
}
Self::ExtractFile { file, dest } => {
mkdir_all(dest).await?;
todo!();
const EXTRACT_FILE_ERROR_MSG: &'static str = "Extracting file failed.";
let dest = tokio::fs::canonicalize(&dest)
.await
.into_diagnostic()
.wrap_err(EXTRACT_FILE_ERROR_MSG)?;
mkdir_all(&dest).await.wrap_err(EXTRACT_FILE_ERROR_MSG)?;
let status = tokio::process::Command::new("tar")
.arg("-xf")
.arg(file.as_os_str())
.current_dir(dest)
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
.await
.into_diagnostic()
.wrap_err(EXTRACT_FILE_ERROR_MSG)?;
ensure!(status.success(), EXTRACT_FILE_ERROR_MSG);
}
Self::ExecuteCommand { file, args } => {
todo!();
const EXECUTE_COMMAND_ERROR_MSG: &'static str = "Executing command failed.";
let status = tokio::process::Command::new(file.as_os_str())
.args(*args)
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
.await
.into_diagnostic()
.wrap_err(EXECUTE_COMMAND_ERROR_MSG)?;
ensure!(status.success(), EXECUTE_COMMAND_ERROR_MSG);
}
Self::CreateShortcut { target, icon, file } => {
mkdir_all(file.parent().ok_or_else(|| miette!("Destination file for shortcut step has no parent."))?).await?;
mkdir_all(
file.parent().ok_or_else(|| {
miette!("Destination file for shortcut step has no parent.")
})?,
)
.await?;
todo!();
}
}
@ -247,5 +291,10 @@ const fn empty_str<'a>() -> &'a str {
}
async fn mkdir_all(path: impl AsRef<Path>) -> Result<()> {
tokio::fs::DirBuilder::new().recursive(true).create(path).await.into_diagnostic().wrap_err("Creating directory and any missing parents failed.")
tokio::fs::DirBuilder::new()
.recursive(true)
.create(path)
.await
.into_diagnostic()
.wrap_err("Creating directory and any missing parents failed.")
}

View File

@ -34,24 +34,58 @@ async fn main() -> Result<()> {
bail!("Could not find or access {}", SWTOOLS_PATH);
}
let utilities = [Pipeline::new(
"Install Notepad++",
vec![
Step::DownloadFile {
file: swtools_path()
.join("temp")
.join("notepad-plus-plus.zip")
.into(),
res: RemoteResource::GitHubArtifact {
repo: "notepad-plus-plus/notepad-plus-plus",
pattern: "npp.{{tag_name_strip_prefix}}.portable.x64.zip",
let nppp_zip = swtools_path().join("temp").join("notepad-plus-plus.zip");
let epp_zip = swtools_path().join("temp").join("explorer-plus-plus.zip");
let utilities = [
Pipeline::new(
"Install Notepad++",
vec![
Step::DownloadFile {
file: nppp_zip.as_path().into(),
res: RemoteResource::GitHubArtifact {
repo: "notepad-plus-plus/notepad-plus-plus",
pattern: "npp.{{tag_name_strip_prefix}}.portable.x64.zip",
},
},
},
],
)];
Step::ExtractFile {
file: nppp_zip.as_path().into(),
dest: swtools_path().join("notepad-plus-plus").into(),
},
],
),
Pipeline::new(
"Install Explorer++",
vec![
Step::DownloadFile {
file: epp_zip.as_path().into(),
res: RemoteResource::Url(
"https://explorerplusplus.com/software/explorer++_1.3.5_x64.zip",
),
},
Step::ExtractFile {
file: epp_zip.as_path().into(),
dest: swtools_path().join("explorer-plus-plus").into(),
},
],
),
Pipeline::new(
"Install Minecraft (Java Edition)",
vec![Step::DownloadFile {
file: swtools_path()
.join("minecraft")
.join("Minecraft.exe")
.into(),
res: RemoteResource::Url("https://launcher.mojang.com/download/Minecraft.exe"),
}],
),
];
let ctx = Context {
reqwest: reqwest::Client::builder().build().into_diagnostic().wrap_err("Could not initialize HTTP client.")?,
reqwest: reqwest::Client::builder()
.build()
.into_diagnostic()
.wrap_err("Could not initialize HTTP client.")?,
};
for utility in utilities {