diff --git a/src/cli/commands/execute.rs b/src/cli/commands/execute.rs new file mode 100644 index 0000000..4787154 --- /dev/null +++ b/src/cli/commands/execute.rs @@ -0,0 +1,88 @@ +use std::{ffi::OsString, process::Command}; + +use anyhow::Context; +use clap::Args; +use semver::VersionReq; + +use crate::cli::{config::read_config, VersionedPackageName}; +use pesde::{ + manifest::target::TargetKind, + names::PackageName, + source::{ + pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource}, + traits::PackageSource, + }, + Project, +}; + +#[derive(Debug, Args)] +pub struct ExecuteCommand { + /// The package name, script name, or path to a script to run + #[arg(index = 1)] + package: VersionedPackageName, + + /// The index URL to use for the package + #[arg(short, long, value_parser = crate::cli::parse_gix_url)] + index: Option, + + /// Arguments to pass to the script + #[arg(index = 2, last = true)] + args: Vec, +} + +impl ExecuteCommand { + pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> { + let index = self + .index + .or_else(|| read_config().ok().map(|c| c.default_index)) + .context("no index specified")?; + let source = PesdePackageSource::new(index); + source + .refresh(&project) + .context("failed to refresh source")?; + + let mut results = source + .resolve( + &PesdeDependencySpecifier { + name: self.package.0, + version: self.package.1.unwrap_or(VersionReq::STAR), + index: None, + target: None, + }, + &project, + TargetKind::Lune, + ) + .context("failed to resolve package")?; + + let (version, pkg_ref) = results.1.pop_last().context("no package found")?; + + log::info!("found package {}@{version}", pkg_ref.name); + + let (fs, target) = source + .download(&pkg_ref, &project, &reqwest) + .context("failed to download package")?; + let bin_path = target.bin_path().context("package has no binary export")?; + + let tmp_dir = project.cas_dir().join(".tmp"); + std::fs::create_dir_all(&tmp_dir).context("failed to create temporary directory")?; + + let tempdir = + tempfile::tempdir_in(tmp_dir).context("failed to create temporary directory")?; + + fs.write_to(tempdir.path(), project.cas_dir(), true) + .context("failed to write package contents")?; + + let status = Command::new("lune") + .arg("run") + .arg(bin_path.to_path(tempdir.path())) + .arg("--") + .args(&self.args) + .current_dir(project.path()) + .status() + .context("failed to run script")?; + + drop(tempdir); + + std::process::exit(status.code().unwrap_or(1)) + } +} diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs index ec5dbb1..fa81bb7 100644 --- a/src/cli/commands/mod.rs +++ b/src/cli/commands/mod.rs @@ -4,6 +4,7 @@ use pesde::Project; mod add; mod auth; mod config; +mod execute; mod init; mod install; mod outdated; @@ -60,6 +61,10 @@ pub enum Subcommand { /// Checks for outdated dependencies Outdated(outdated::OutdatedCommand), + + /// Executes a binary package without needing to be run in a project directory + #[clap(name = "x", visible_alias = "execute", visible_alias = "exec")] + Execute(execute::ExecuteCommand), } impl Subcommand { @@ -88,6 +93,7 @@ impl Subcommand { update.run(project, multi, reqwest) } Subcommand::Outdated(outdated) => outdated.run(project), + Subcommand::Execute(execute) => execute.run(project, reqwest), } } } diff --git a/src/cli/commands/publish.rs b/src/cli/commands/publish.rs index 9f845ae..7128086 100644 --- a/src/cli/commands/publish.rs +++ b/src/cli/commands/publish.rs @@ -12,7 +12,7 @@ use tempfile::tempfile; use pesde::{ manifest::target::Target, scripts::ScriptName, - source::{pesde::PesdePackageSource, traits::PackageSource}, + source::{pesde::PesdePackageSource, specifiers::DependencySpecifiers, traits::PackageSource}, Project, DEFAULT_INDEX_NAME, MANIFEST_FILE_NAME, }; @@ -318,6 +318,26 @@ impl PublishCommand { ); } + let dependencies = manifest + .all_dependencies() + .context("failed to get dependencies")?; + if !config.git_allowed + && dependencies + .iter() + .any(|(_, (spec, _))| matches!(spec, DependencySpecifiers::Git(_))) + { + anyhow::bail!("git dependencies are not allowed on this index"); + } + + #[cfg(feature = "wally-compat")] + if !config.wally_allowed + && dependencies + .iter() + .any(|(_, (spec, _))| matches!(spec, DependencySpecifiers::Wally(_))) + { + anyhow::bail!("wally dependencies are not allowed on this index"); + } + if self.dry_run { std::fs::write("package.tar.gz", archive)?; diff --git a/src/cli/commands/run.rs b/src/cli/commands/run.rs index 8da89ff..4a2302c 100644 --- a/src/cli/commands/run.rs +++ b/src/cli/commands/run.rs @@ -1,12 +1,15 @@ -use crate::cli::IsUpToDate; +use std::{ffi::OsString, path::PathBuf, process::Command}; + use anyhow::Context; use clap::Args; +use relative_path::RelativePathBuf; + use pesde::{ names::{PackageName, PackageNames}, - scripts::execute_script, Project, PACKAGES_CONTAINER_NAME, }; -use relative_path::RelativePathBuf; + +use crate::cli::IsUpToDate; #[derive(Debug, Args)] pub struct RunCommand { @@ -16,11 +19,24 @@ pub struct RunCommand { /// Arguments to pass to the script #[arg(index = 2, last = true)] - args: Vec, + args: Vec, } impl RunCommand { pub fn run(self, project: Project) -> anyhow::Result<()> { + let run = |path: PathBuf| { + let status = Command::new("lune") + .arg("run") + .arg(path) + .arg("--") + .args(&self.args) + .current_dir(project.path()) + .status() + .expect("failed to run script"); + + std::process::exit(status.code().unwrap_or(1)) + }; + if let Ok(pkg_name) = self.package_or_script.parse::() { let graph = if project.is_up_to_date(true)? { project.deser_lockfile()?.graph @@ -51,31 +67,13 @@ impl RunCommand { version_id.version(), ); - let path = bin_path.to_path(&container_folder); - - execute_script( - Some(pkg_name.as_str().1), - &path, - &self.args, - project.path(), - false, - ) - .context("failed to execute script")?; + run(bin_path.to_path(&container_folder)) } } if let Ok(manifest) = project.deser_manifest() { if let Some(script_path) = manifest.scripts.get(&self.package_or_script) { - execute_script( - Some(&self.package_or_script), - &script_path.to_path(project.path()), - &self.args, - project.path(), - false, - ) - .context("failed to execute script")?; - - return Ok(()); + run(script_path.to_path(project.path())) } }; @@ -86,8 +84,7 @@ impl RunCommand { anyhow::bail!("path does not exist: {}", path.display()); } - execute_script(None, &path, &self.args, project.path(), false) - .context("failed to execute script")?; + run(path); Ok(()) } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4d24956..b525a62 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -85,9 +85,11 @@ impl IsUpToDate for Project { } #[derive(Debug, Clone)] -struct VersionedPackageName(PackageNames, Option); +struct VersionedPackageName(N, Option); -impl, E: Into> FromStr for VersionedPackageName { +impl, E: Into, N: FromStr, F: Into> + FromStr for VersionedPackageName +{ type Err = anyhow::Error; fn from_str(s: &str) -> Result { @@ -99,7 +101,10 @@ impl, E: Into> FromStr for VersionedPackageNa .transpose() .map_err(Into::into)?; - Ok(VersionedPackageName(name.parse()?, version)) + Ok(VersionedPackageName( + name.parse().map_err(Into::into)?, + version, + )) } } diff --git a/src/linking/mod.rs b/src/linking/mod.rs index 03e3a7b..0368950 100644 --- a/src/linking/mod.rs +++ b/src/linking/mod.rs @@ -98,7 +98,7 @@ impl Project { }; execute_script( - Some(&script_name), + ScriptName::RobloxSyncConfigGenerator, &script_path.to_path(self.path()), build_files, &container_folder, diff --git a/src/scripts.rs b/src/scripts.rs index 1f463f0..8991d5b 100644 --- a/src/scripts.rs +++ b/src/scripts.rs @@ -29,9 +29,8 @@ impl Display for ScriptName { } } -/// Executes a script with the given arguments -pub fn execute_script, S: AsRef, P: AsRef>( - script_name: Option<&str>, +pub(crate) fn execute_script, S: AsRef, P: AsRef>( + script_name: ScriptName, script_path: &Path, args: A, cwd: P, @@ -52,11 +51,7 @@ pub fn execute_script, S: AsRef, P: AsRef let stdout = BufReader::new(child.stdout.take().unwrap()); let stderr = BufReader::new(child.stderr.take().unwrap()); - let script = match script_name { - Some(script) => script.to_string(), - None => script_path.to_string_lossy().to_string(), - }; - + let script = script_name.to_string(); let script_2 = script.to_string(); spawn(move || { diff --git a/src/source/wally/compat_util.rs b/src/source/wally/compat_util.rs index 1b5dcb7..40d9f7f 100644 --- a/src/source/wally/compat_util.rs +++ b/src/source/wally/compat_util.rs @@ -32,7 +32,7 @@ pub(crate) fn find_lib_path( }; let result = execute_script( - Some(&ScriptName::SourcemapGenerator.to_string()), + ScriptName::SourcemapGenerator, &script_path.to_path(&project.path), ["--wally"], cwd,