diff --git a/src/cli/commands/execute.rs b/src/cli/commands/execute.rs index 8191533..c0f5ae7 100644 --- a/src/cli/commands/execute.rs +++ b/src/cli/commands/execute.rs @@ -1,11 +1,8 @@ -use std::{ffi::OsString, process::Command}; - +use crate::cli::{config::read_config, VersionedPackageName}; use anyhow::Context; use clap::Args; -use semver::VersionReq; - -use crate::cli::{config::read_config, VersionedPackageName}; use pesde::{ + linking::generator::generate_bin_linking_module, manifest::target::TargetKind, names::PackageName, source::{ @@ -14,6 +11,8 @@ use pesde::{ }, Project, }; +use semver::VersionReq; +use std::{env::current_dir, ffi::OsString, io::Write, process::Command}; #[derive(Debug, Args)] pub struct ExecuteCommand { @@ -72,15 +71,28 @@ impl ExecuteCommand { fs.write_to(tempdir.path(), project.cas_dir(), true) .context("failed to write package contents")?; + 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(bin_path.to_path(tempdir.path())) + .arg(caller.path()) .arg("--") .args(&self.args) - .current_dir(project.package_dir()) + .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)) diff --git a/src/cli/commands/init.rs b/src/cli/commands/init.rs index 36fe9a2..49af6a0 100644 --- a/src/cli/commands/init.rs +++ b/src/cli/commands/init.rs @@ -9,22 +9,17 @@ use pesde::{ errors::ManifestReadError, names::PackageName, scripts::ScriptName, Project, DEFAULT_INDEX_NAME, }; -use crate::cli::config::read_config; +use crate::cli::{config::read_config, HOME_DIR}; #[derive(Debug, Args)] pub struct InitCommand {} fn script_contents(path: &Path) -> String { format!( - concat!( - r#"local process = require("@lune/process") + r#"local process = require("@lune/process") local home_dir = if process.os == "windows" then process.env.userprofile else process.env.HOME -require(home_dir .. ""#, - "/.", - env!("CARGO_PKG_NAME"), - r#"/scripts/{}")"#, - ), +require(home_dir .. "/{HOME_DIR}/scripts/{}"#, path.display() ) } diff --git a/src/cli/commands/install.rs b/src/cli/commands/install.rs index 474ce90..560026d 100644 --- a/src/cli/commands/install.rs +++ b/src/cli/commands/install.rs @@ -43,7 +43,6 @@ fn bin_link_file(alias: &str) -> String { let prefix = String::new(); #[cfg(unix)] let prefix = "#!/usr/bin/env -S lune run\n"; - // TODO: reimplement workspace support in this format!( r#"{prefix}local process = require("@lune/process") local fs = require("@lune/fs") diff --git a/src/cli/commands/run.rs b/src/cli/commands/run.rs index d096ce6..fd2c3ea 100644 --- a/src/cli/commands/run.rs +++ b/src/cli/commands/run.rs @@ -1,15 +1,14 @@ -use std::{ffi::OsString, path::PathBuf, process::Command}; - +use crate::cli::up_to_date_lockfile; use anyhow::Context; use clap::Args; -use relative_path::RelativePathBuf; - -use crate::cli::up_to_date_lockfile; use pesde::{ + linking::generator::generate_bin_linking_module, names::{PackageName, PackageNames}, source::traits::PackageRef, Project, PACKAGES_CONTAINER_NAME, }; +use relative_path::RelativePathBuf; +use std::{env::current_dir, ffi::OsString, io::Write, path::PathBuf, process::Command}; #[derive(Debug, Args)] pub struct RunCommand { @@ -25,15 +24,28 @@ pub struct RunCommand { impl RunCommand { pub fn run(self, project: Project) -> anyhow::Result<()> { let run = |path: PathBuf| { + let mut caller = tempfile::NamedTempFile::new().expect("failed to create tempfile"); + caller + .write_all( + generate_bin_linking_module( + project.package_dir(), + &format!("{:?}", path.to_string_lossy()), + ) + .as_bytes(), + ) + .expect("failed to write to tempfile"); + let status = Command::new("lune") .arg("run") - .arg(path) + .arg(caller.path()) .arg("--") .args(&self.args) - .current_dir(project.package_dir()) + .current_dir(current_dir().expect("failed to get current directory")) .status() .expect("failed to run script"); + drop(caller); + std::process::exit(status.code().unwrap_or(1)) }; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 518750b..1b1f93a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -18,7 +18,7 @@ pub mod auth; pub mod commands; pub mod config; pub mod files; -pub mod scripts; +pub mod repos; pub mod version; pub const HOME_DIR: &str = concat!(".", env!("CARGO_PKG_NAME")); diff --git a/src/cli/repos.rs b/src/cli/repos.rs new file mode 100644 index 0000000..692a1f4 --- /dev/null +++ b/src/cli/repos.rs @@ -0,0 +1,114 @@ +use crate::{ + cli::{config::read_config, home_dir}, + util::authenticate_conn, +}; +use anyhow::Context; +use gix::remote::Direction; +use pesde::Project; +use std::path::Path; + +fn update_repo>( + name: &str, + path: P, + url: gix::Url, + project: &Project, +) -> anyhow::Result<()> { + let path = path.as_ref(); + if path.exists() { + let repo = gix::open(path).context(format!("failed to open {name} repository"))?; + + let remote = repo + .find_default_remote(Direction::Fetch) + .context(format!("missing default remote of {name} repository"))? + .context(format!( + "failed to find default remote of {name} repository" + ))?; + + let mut connection = remote.connect(Direction::Fetch).context(format!( + "failed to connect to default remote of {name} repository" + ))?; + + authenticate_conn(&mut connection, project.auth_config()); + + let results = connection + .prepare_fetch(gix::progress::Discard, Default::default()) + .context(format!("failed to prepare {name} repository fetch"))? + .receive(gix::progress::Discard, &false.into()) + .context(format!("failed to receive new {name} repository contents"))?; + + let remote_ref = results + .ref_map + .remote_refs + .first() + .context(format!("failed to get remote refs of {name} repository"))?; + + let unpacked = remote_ref.unpack(); + let oid = unpacked + .1 + .or(unpacked.2) + .context("couldn't find oid in remote ref")?; + + let tree = repo + .find_object(oid) + .context(format!("failed to find {name} repository tree"))? + .peel_to_tree() + .context(format!("failed to peel {name} repository object to tree"))?; + + let mut index = gix::index::File::from_state( + gix::index::State::from_tree(&tree.id, &repo.objects, Default::default()).context( + format!("failed to create index state from {name} repository tree"), + )?, + repo.index_path(), + ); + + let opts = gix::worktree::state::checkout::Options { + overwrite_existing: true, + destination_is_initially_empty: false, + ..Default::default() + }; + + gix::worktree::state::checkout( + &mut index, + repo.work_dir().context(format!("{name} repo is bare"))?, + repo.objects + .clone() + .into_arc() + .context("failed to clone objects")?, + &gix::progress::Discard, + &gix::progress::Discard, + &false.into(), + opts, + ) + .context(format!("failed to checkout {name} repository"))?; + + index + .write(gix::index::write::Options::default()) + .context("failed to write index")?; + } else { + std::fs::create_dir_all(path).context(format!("failed to create {name} directory"))?; + + gix::prepare_clone(url, path) + .context(format!("failed to prepare {name} repository clone"))? + .fetch_then_checkout(gix::progress::Discard, &false.into()) + .context(format!("failed to fetch and checkout {name} repository"))? + .0 + .main_worktree(gix::progress::Discard, &false.into()) + .context(format!("failed to set {name} repository as main worktree"))?; + }; + + Ok(()) +} + +pub fn update_repo_dependencies(project: &Project) -> anyhow::Result<()> { + let home_dir = home_dir()?; + let config = read_config()?; + + update_repo( + "scripts", + home_dir.join("scripts"), + config.scripts_repo, + project, + )?; + + Ok(()) +} diff --git a/src/cli/scripts.rs b/src/cli/scripts.rs deleted file mode 100644 index 546712a..0000000 --- a/src/cli/scripts.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ - cli::{config::read_config, home_dir}, - util::authenticate_conn, -}; -use anyhow::Context; -use gix::remote::Direction; -use pesde::Project; - -pub fn update_scripts_folder(project: &Project) -> anyhow::Result<()> { - let scripts_dir = home_dir()?.join("scripts"); - - if scripts_dir.exists() { - let repo = gix::open(&scripts_dir).context("failed to open scripts repository")?; - - let remote = repo - .find_default_remote(Direction::Fetch) - .context("missing default remote of scripts repository")? - .context("failed to find default remote of scripts repository")?; - - let mut connection = remote - .connect(Direction::Fetch) - .context("failed to connect to default remote of scripts repository")?; - - authenticate_conn(&mut connection, project.auth_config()); - - let results = connection - .prepare_fetch(gix::progress::Discard, Default::default()) - .context("failed to prepare scripts repository fetch")? - .receive(gix::progress::Discard, &false.into()) - .context("failed to receive new scripts repository contents")?; - - let remote_ref = results - .ref_map - .remote_refs - .first() - .context("failed to get remote refs of scripts repository")?; - - let unpacked = remote_ref.unpack(); - let oid = unpacked - .1 - .or(unpacked.2) - .context("couldn't find oid in remote ref")?; - - let tree = repo - .find_object(oid) - .context("failed to find scripts repository tree")? - .peel_to_tree() - .context("failed to peel scripts repository object to tree")?; - - let mut index = gix::index::File::from_state( - gix::index::State::from_tree(&tree.id, &repo.objects, Default::default()) - .context("failed to create index state from scripts repository tree")?, - repo.index_path(), - ); - - let opts = gix::worktree::state::checkout::Options { - overwrite_existing: true, - destination_is_initially_empty: false, - ..Default::default() - }; - - gix::worktree::state::checkout( - &mut index, - repo.work_dir().context("scripts repo is bare")?, - repo.objects - .clone() - .into_arc() - .context("failed to clone objects")?, - &gix::progress::Discard, - &gix::progress::Discard, - &false.into(), - opts, - ) - .context("failed to checkout scripts repository")?; - - index - .write(gix::index::write::Options::default()) - .context("failed to write index")?; - } else { - std::fs::create_dir_all(&scripts_dir).context("failed to create scripts directory")?; - - let cli_config = read_config()?; - - gix::prepare_clone(cli_config.scripts_repo, &scripts_dir) - .context("failed to prepare scripts repository clone")? - .fetch_then_checkout(gix::progress::Discard, &false.into()) - .context("failed to fetch and checkout scripts repository")? - .0 - .main_worktree(gix::progress::Discard, &false.into()) - .context("failed to set scripts repository as main worktree")?; - }; - - Ok(()) -} diff --git a/src/linking/generator.rs b/src/linking/generator.rs index 6b7a1f9..8a3a9dd 100644 --- a/src/linking/generator.rs +++ b/src/linking/generator.rs @@ -125,8 +125,12 @@ pub fn get_lib_require_path( } /// Generate a linking module for a binary -pub fn generate_bin_linking_module(path: &str) -> String { - format!("return require({path})") +pub fn generate_bin_linking_module>(package_root: P, require_path: &str) -> String { + format!( + r#"_G.PESDE_ROOT = {:?} +return require({require_path})"#, + package_root.as_ref().to_string_lossy() + ) } /// Get the require path for a binary diff --git a/src/linking/mod.rs b/src/linking/mod.rs index 46d3785..7212b07 100644 --- a/src/linking/mod.rs +++ b/src/linking/mod.rs @@ -139,14 +139,15 @@ impl Project { version_id.version(), ); - if let Some((alias, types)) = package_types - .get(name) - .and_then(|v| v.get(version_id)) - .and_then(|types| { - node.node.direct.as_ref().map(|(alias, _)| (alias, types)) - }) - { - if let Some(lib_file) = node.target.lib_path() { + if let Some((alias, _)) = &node.node.direct.as_ref() { + if let Some((lib_file, types)) = + node.target.lib_path().and_then(|lib_file| { + package_types + .get(name) + .and_then(|v| v.get(version_id)) + .map(|types| (lib_file, types)) + }) + { write_cas( base_folder.join(format!("{alias}.luau")), self.cas_dir(), @@ -168,6 +169,7 @@ impl Project { base_folder.join(format!("{alias}.bin.luau")), self.cas_dir(), &generator::generate_bin_linking_module( + &container_folder, &generator::get_bin_require_path( &base_folder, bin_file, diff --git a/src/main.rs b/src/main.rs index 9e5f3c2..82bd83b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use crate::cli::{ auth::get_token, config::read_config, home_dir, - scripts::update_scripts_folder, + repos::update_repo_dependencies, version::{check_for_updates, current_version, get_or_download_version, max_installed_version}, HOME_DIR, }; @@ -258,32 +258,18 @@ fn run() -> anyhow::Result<()> { std::process::exit(status.code().unwrap()); } - match check_for_updates(&reqwest) { - Ok(_) => {} - Err(e) => { - println!( - "{}", - format!("failed to check for updates: {e}\n\n").red().bold() - ); - } - } - - match update_scripts_folder(&project) { - Ok(_) => {} - Err(e) => { - println!( - "{}", - format!("failed to update scripts: {e}\n\n").red().bold() - ); - } - } + display_err(check_for_updates(&reqwest), " while checking for updates"); + display_err( + update_repo_dependencies(&project), + " while updating repository dependencies", + ); Cli::parse().subcommand.run(project, multi, reqwest) } -fn main() { - if let Err(err) = run() { - eprintln!("{}: {err}\n", "error".red().bold()); +fn display_err(result: anyhow::Result<()>, prefix: &str) { + if let Err(err) = result { + eprintln!("{}: {err}\n", format!("error{prefix}").red().bold()); let cause = err.chain().skip(1).collect::>(); @@ -309,7 +295,14 @@ fn main() { eprintln!("\n{}: not captured", "backtrace".yellow().bold()); } } + } +} +fn main() { + let result = run(); + let is_err = result.is_err(); + display_err(result, ""); + if is_err { std::process::exit(1); } }