feat: implement execute command

This commit is contained in:
daimond113 2024-08-11 17:27:51 +02:00
parent 79bbe11cab
commit 09307276b0
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
8 changed files with 151 additions and 40 deletions

View file

@ -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<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 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))
}
}

View file

@ -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),
}
}
}

View file

@ -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)?;

View file

@ -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<String>,
args: Vec<OsString>,
}
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::<PackageName>() {
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(())
}

View file

@ -85,9 +85,11 @@ impl IsUpToDate for Project {
}
#[derive(Debug, Clone)]
struct VersionedPackageName<T: FromStr = VersionId>(PackageNames, Option<T>);
struct VersionedPackageName<V: FromStr = VersionId, N: FromStr = PackageNames>(N, Option<V>);
impl<T: FromStr<Err = E>, E: Into<anyhow::Error>> FromStr for VersionedPackageName<T> {
impl<V: FromStr<Err = E>, E: Into<anyhow::Error>, N: FromStr<Err = F>, F: Into<anyhow::Error>>
FromStr for VersionedPackageName<V, N>
{
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -99,7 +101,10 @@ impl<T: FromStr<Err = E>, E: Into<anyhow::Error>> FromStr for VersionedPackageNa
.transpose()
.map_err(Into::into)?;
Ok(VersionedPackageName(name.parse()?, version))
Ok(VersionedPackageName(
name.parse().map_err(Into::into)?,
version,
))
}
}

View file

@ -98,7 +98,7 @@ impl Project {
};
execute_script(
Some(&script_name),
ScriptName::RobloxSyncConfigGenerator,
&script_path.to_path(self.path()),
build_files,
&container_folder,

View file

@ -29,9 +29,8 @@ impl Display for ScriptName {
}
}
/// Executes a script with the given arguments
pub fn execute_script<A: IntoIterator<Item = S>, S: AsRef<OsStr>, P: AsRef<Path>>(
script_name: Option<&str>,
pub(crate) fn execute_script<A: IntoIterator<Item = S>, S: AsRef<OsStr>, P: AsRef<Path>>(
script_name: ScriptName,
script_path: &Path,
args: A,
cwd: P,
@ -52,11 +51,7 @@ pub fn execute_script<A: IntoIterator<Item = S>, S: AsRef<OsStr>, P: AsRef<Path>
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 || {

View file

@ -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,