Compare commits

...

3 Commits

Author SHA1 Message Date
Michael Pfaff bc44b249f4
Fix step logging 2022-06-10 13:30:48 -04:00
Michael Pfaff ceeaed0b8d
Don't download file if it already exists. 2022-06-10 13:23:07 -04:00
Michael Pfaff 12991439fa
Install Psiphon3 VPN 2022-06-10 13:16:30 -04:00
2 changed files with 103 additions and 60 deletions

View File

@ -72,76 +72,89 @@ impl<'a> Step<'a> {
pub async fn invoke(&self, ctx: &Context) -> Result<()> { pub async fn invoke(&self, ctx: &Context) -> Result<()> {
match self { match self {
Self::DownloadFile { res, file } => { Self::DownloadFile { res, file } => {
println!( if file.exists() {
"Downloading file {file} from {res:?}...", println!(
file = file.to_str().unwrap_or("<NON UTF-8>") "File {file} already downloaded.",
); 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 = } else {
"Writing the remote resource to disk failed."; println!(
let url = match res { "Downloading file {file} from {res:?}...",
RemoteResource::Url(url) => url::Url::parse(url) file = file.to_str().unwrap_or("<NON UTF-8>")
.into_diagnostic() );
.wrap_err("Invalid url for download step.")?, const FETCH_FILE_ERROR_MSG: &'static str =
RemoteResource::GitHubArtifact { repo, pattern } => { "Fetching the remote resource failed.";
let mut release = fetch_latest_release(&ctx.reqwest, repo).await?; const WRITE_FILE_ERROR_MSG: &'static str =
let pattern = ramhorns::Template::new(*pattern) "Writing the remote resource to disk failed.";
let url = match res {
RemoteResource::Url(url) => url::Url::parse(url)
.into_diagnostic() .into_diagnostic()
.wrap_err("Invalid pattern for artifact matching in download step.")?; .wrap_err("Invalid url for download step.")?,
release.meta.tag_name_strip_prefix = release RemoteResource::GitHubArtifact { repo, pattern } => {
.meta let mut release = fetch_latest_release(&ctx.reqwest, repo).await?;
.tag_name let pattern = ramhorns::Template::new(*pattern)
.strip_prefix('v') .into_diagnostic()
.unwrap_or(&release.meta.tag_name); .wrap_err(
let asset_name = pattern.render(&release.meta); "Invalid pattern for artifact matching in download step.",
let artifact = release.assets.into_iter().filter(move |asset| asset.name == asset_name).next().ok_or_else(|| miette!("No artifact of the latest release matched the pattern in download step."))?; )?;
url::Url::parse(&artifact.browser_download_url) release.meta.tag_name_strip_prefix = release
.into_diagnostic() .meta
.wrap_err( .tag_name
"Invalid url returned by GitHub for latest release artifact.", .strip_prefix('v')
)? .unwrap_or(&release.meta.tag_name);
} let asset_name = pattern.render(&release.meta);
}; let artifact = release.assets.into_iter().filter(move |asset| asset.name == asset_name).next().ok_or_else(|| miette!("No artifact of the latest release matched the pattern in download step."))?;
let mut resp = ctx url::Url::parse(&artifact.browser_download_url)
.reqwest .into_diagnostic()
.get(url) .wrap_err(
.header("User-Agent", USER_AGENT_STR) "Invalid url returned by GitHub for latest release artifact.",
.send() )?
.await }
.into_diagnostic() };
.wrap_err(FETCH_FILE_ERROR_MSG)?; let mut resp = ctx
let _content_length = resp.content_length(); .reqwest
mkdir_all( .get(url)
file.parent().ok_or_else(|| { .header("User-Agent", USER_AGENT_STR)
miette!("Destination file for download step has no parent.") .send()
})?,
)
.await?;
let mut writer = tokio::io::BufWriter::new(
tokio::fs::File::create(file.as_os_str())
.await .await
.into_diagnostic() .into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?, .wrap_err(FETCH_FILE_ERROR_MSG)?;
); let _content_length = resp.content_length();
while let Some(mut chunk) = resp mkdir_all(file.parent().ok_or_else(|| {
.chunk() miette!("Destination file for download step has no parent.")
.await })?)
.into_diagnostic() .await?;
.wrap_err(FETCH_FILE_ERROR_MSG)? let mut writer = tokio::io::BufWriter::new(
{ tokio::fs::File::create(file.as_os_str())
.await
.into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?,
);
while let Some(mut chunk) = resp
.chunk()
.await
.into_diagnostic()
.wrap_err(FETCH_FILE_ERROR_MSG)?
{
writer
.write_all_buf(&mut chunk)
.await
.into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?;
}
writer writer
.write_all_buf(&mut chunk) .flush()
.await .await
.into_diagnostic() .into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?; .wrap_err(WRITE_FILE_ERROR_MSG)?;
} }
writer
.flush()
.await
.into_diagnostic()
.wrap_err(WRITE_FILE_ERROR_MSG)?;
} }
Self::ExtractFile { file, dest } => { Self::ExtractFile { file, dest } => {
println!(
"Extracting {file} to {dest}...",
file = file.to_str().unwrap_or("<NON UTF-8>"),
dest = dest.to_str().unwrap_or("<NON UTF-8>")
);
const EXTRACT_FILE_ERROR_MSG: &'static str = "Extracting file failed."; const EXTRACT_FILE_ERROR_MSG: &'static str = "Extracting file failed.";
mkdir_all(&dest).await.wrap_err(EXTRACT_FILE_ERROR_MSG)?; mkdir_all(&dest).await.wrap_err(EXTRACT_FILE_ERROR_MSG)?;
let dest = tokio::fs::canonicalize(&dest) let dest = tokio::fs::canonicalize(&dest)
@ -161,6 +174,11 @@ impl<'a> Step<'a> {
ensure!(status.success(), EXTRACT_FILE_ERROR_MSG); ensure!(status.success(), EXTRACT_FILE_ERROR_MSG);
} }
Self::ExecuteCommand { file, args } => { Self::ExecuteCommand { file, args } => {
println!(
"Executing command `{file} {args}`...",
file = file.to_str().unwrap_or("<NON UTF-8>"),
args = args.into_iter().map(|s| *s).collect::<String>(),
);
const EXECUTE_COMMAND_ERROR_MSG: &'static str = "Executing command failed."; const EXECUTE_COMMAND_ERROR_MSG: &'static str = "Executing command failed.";
let status = tokio::process::Command::new(file.as_os_str()) let status = tokio::process::Command::new(file.as_os_str())
.args(*args) .args(*args)
@ -173,6 +191,10 @@ impl<'a> Step<'a> {
ensure!(status.success(), EXECUTE_COMMAND_ERROR_MSG); ensure!(status.success(), EXECUTE_COMMAND_ERROR_MSG);
} }
Self::CreateShortcut { target, file } => { Self::CreateShortcut { target, file } => {
println!(
"Creating shortcut to {target:?} at {file}...",
file = file.to_str().unwrap_or("<NON UTF-8>")
);
const CREATE_SHORTCUT_ERROR_MSG: &'static str = "Creating shortcut failed."; const CREATE_SHORTCUT_ERROR_MSG: &'static str = "Creating shortcut failed.";
mkdir_all( mkdir_all(
file.parent().ok_or_else(|| { file.parent().ok_or_else(|| {

View File

@ -46,6 +46,9 @@ async fn main() -> Result<()> {
let minecraft_dir = swtools_path().join("minecraft"); let minecraft_dir = swtools_path().join("minecraft");
let psiphon_dir = swtools_path().join("psiphon");
let psiphon_bin = psiphon_dir.join("psiphon3.exe");
let utilities = [ let utilities = [
Pipeline::new( Pipeline::new(
"Install Notepad++", "Install Notepad++",
@ -94,6 +97,24 @@ async fn main() -> Result<()> {
}, },
], ],
), ),
Pipeline::new(
"Install Psiphon VPN",
vec![
Step::DownloadFile {
file: psiphon_bin.as_path().into(),
res: RemoteResource::Url(
"https://s3.amazonaws.com/f58p-mqce-k1yj/psiphon3.exe",
),
},
Step::CreateShortcut {
target: ShortcutTarget::Executable {
file: psiphon_bin.as_path().into(),
args: "",
},
file: desktop_path().join("Psiphon3.lnk").into(),
},
],
),
Pipeline::new( Pipeline::new(
"Install Minecraft (Java Edition)", "Install Minecraft (Java Edition)",
vec![ vec![