pesde/src/cli/commands/execute.rs
daimond113 52603ea43e
Some checks are pending
Test & Lint / lint (push) Waiting to run
feat: switch to tracing for logging
2024-12-16 23:00:37 +01:00

167 lines
5.3 KiB
Rust

use crate::cli::{config::read_config, progress_bar, VersionedPackageName};
use anyhow::Context;
use clap::Args;
use fs_err::tokio as fs;
use pesde::{
linking::generator::generate_bin_linking_module,
manifest::target::TargetKind,
names::PackageName,
source::{
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
traits::PackageSource,
},
Project,
};
use semver::VersionReq;
use std::{
collections::HashSet, env::current_dir, ffi::OsString, io::Write, process::Command, sync::Arc,
};
use tokio::sync::Mutex;
#[derive(Debug, Args)]
pub struct ExecuteCommand {
/// The package name, script name, or path to a script to run
#[arg(index = 1)]
package: VersionedPackageName<VersionReq, PackageName>,
/// The index URL to use for the package
#[arg(short, long, value_parser = crate::cli::parse_gix_url)]
index: Option<gix::Url>,
/// Arguments to pass to the script
#[arg(index = 2, last = true)]
args: Vec<OsString>,
}
impl ExecuteCommand {
pub async fn run(self, project: Project, reqwest: reqwest::Client) -> anyhow::Result<()> {
let index = match self.index {
Some(index) => Some(index),
None => read_config().await.ok().map(|c| c.default_index),
}
.context("no index specified")?;
let source = PesdePackageSource::new(index);
source
.refresh(&project)
.await
.context("failed to refresh source")?;
let version_req = self.package.1.unwrap_or(VersionReq::STAR);
let Some((version, pkg_ref)) = ('finder: {
let specifier = PesdeDependencySpecifier {
name: self.package.0.clone(),
version: version_req.clone(),
index: None,
target: None,
};
if let Some(res) = source
.resolve(&specifier, &project, TargetKind::Lune, &mut HashSet::new())
.await
.context("failed to resolve package")?
.1
.pop_last()
{
break 'finder Some(res);
}
source
.resolve(&specifier, &project, TargetKind::Luau, &mut HashSet::new())
.await
.context("failed to resolve package")?
.1
.pop_last()
}) else {
anyhow::bail!(
"no Lune or Luau package could be found for {}@{version_req}",
self.package.0,
);
};
println!("using {}@{version}", pkg_ref.name);
let tmp_dir = project.cas_dir().join(".tmp");
fs::create_dir_all(&tmp_dir)
.await
.context("failed to create temporary directory")?;
let tempdir =
tempfile::tempdir_in(tmp_dir).context("failed to create temporary directory")?;
let project = Project::new(
tempdir.path(),
None::<std::path::PathBuf>,
project.data_dir(),
project.cas_dir(),
project.auth_config().clone(),
);
let (fs, target) = source
.download(&pkg_ref, &project, &reqwest)
.await
.context("failed to download package")?;
let bin_path = target.bin_path().context("package has no binary export")?;
fs.write_to(tempdir.path(), project.cas_dir(), true)
.await
.context("failed to write package contents")?;
let mut refreshed_sources = HashSet::new();
let graph = project
.dependency_graph(None, &mut refreshed_sources, true)
.await
.context("failed to build dependency graph")?;
let graph = Arc::new(graph);
let (rx, downloaded_graph) = project
.download_and_link(
&graph,
&Arc::new(Mutex::new(refreshed_sources)),
&reqwest,
true,
true,
|_| async { Ok::<_, std::io::Error>(()) },
)
.await
.context("failed to download dependencies")?;
progress_bar(
graph.values().map(|versions| versions.len() as u64).sum(),
rx,
"📥 ".to_string(),
"downloading dependencies".to_string(),
"downloaded dependencies".to_string(),
)
.await?;
downloaded_graph
.await
.context("failed to download & link dependencies")?;
let mut caller =
tempfile::NamedTempFile::new_in(tempdir.path()).context("failed to create tempfile")?;
caller
.write_all(
generate_bin_linking_module(
tempdir.path(),
&format!("{:?}", bin_path.to_path(tempdir.path())),
)
.as_bytes(),
)
.context("failed to write to tempfile")?;
let status = Command::new("lune")
.arg("run")
.arg(caller.path())
.arg("--")
.args(&self.args)
.current_dir(current_dir().context("failed to get current directory")?)
.status()
.context("failed to run script")?;
drop(caller);
drop(tempdir);
std::process::exit(status.code().unwrap_or(1))
}
}