Skip to content

Commit

Permalink
feat!(rocks-bin/build): project lockfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Jan 5, 2025
1 parent f5acbb2 commit 67a1a1f
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 150 deletions.
101 changes: 28 additions & 73 deletions rocks-bin/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use eyre::eyre;
use inquire::Confirm;
use eyre::{Context, OptionExt};
use itertools::Itertools;
use std::{path::PathBuf, sync::Arc};
use std::sync::Arc;

use clap::Args;
use eyre::Result;
Expand All @@ -10,88 +9,38 @@ use rocks_lib::{
config::Config,
lockfile::PinnedState,
operations::Install,
package::{PackageName, PackageReq},
package::PackageName,
progress::MultiProgress,
rockspec::Rockspec,
tree::{RockMatches, Tree},
project::Project,
remote_package_db::RemotePackageDB,
};

#[derive(Args, Default)]
pub struct Build {
rockspec_path: Option<PathBuf>,

/// Whether to pin the dependencies.
#[arg(long)]
pin: bool,

/// Ignore the project's existing lockfile.
#[arg(long)]
ignore_lockfile: bool,

/// Do not create a lockfile.
#[arg(long)]
force: bool,
no_lock: bool,
}

pub async fn build(data: Build, config: Config) -> Result<()> {
let project = Project::current()?.ok_or_eyre("Not in a project!")?;
let pin = PinnedState::from(data.pin);

let rockspec_path = data.rockspec_path.map_or_else(|| {
// Try to infer the rockspec the user meant.

let cwd = std::env::current_dir()?;

let rockspec_paths = walkdir::WalkDir::new(cwd)
.max_depth(1)
.same_file_system(true)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry.file_type().is_file()
&& entry.path().extension().map(|ext| ext.to_str()) == Some(Some("rockspec"))
})
.collect_vec();

let rockspec_count = rockspec_paths.len();

match rockspec_count {
0 => Err(eyre!("No rockspec files found in the current directory!")),
1 => Ok(rockspec_paths.first().unwrap().clone().into_path()),
_ => Err(eyre!("Could not infer the rockspec to use! There are {} rockspecs in the current directory, please provide a path to the one you'd like to use.", rockspec_count)),
}
}, Ok)?;

if rockspec_path
.extension()
.map(|ext| ext != "rockspec")
.unwrap_or(true)
{
return Err(eyre!("Provided path is not a valid rockspec!"));
}

let rockspec = std::fs::read_to_string(rockspec_path)?;
let rockspec = Rockspec::new(&rockspec)?;

let lua_version = rockspec.lua_version_from_config(&config)?;

let tree = Tree::new(config.tree().clone(), lua_version)?;

let build_behaviour = match tree.match_rocks_and(
&PackageReq::new(
rockspec.package.to_string(),
Some(rockspec.version.to_string()),
)?,
|rock| pin == rock.pinned(),
)? {
RockMatches::Single(_) | RockMatches::Many(_) if !data.force => {
if Confirm::new(&format!(
"Package {} already exists. Overwrite?",
rockspec.package,
))
.with_default(false)
.prompt()?
{
BuildBehaviour::Force
} else {
return Ok(());
}
}
_ => BuildBehaviour::from(data.force),
let package_db = match project.lockfile()? {
None => RemotePackageDB::from_config(&config).await?,
Some(_) if data.ignore_lockfile => RemotePackageDB::from_config(&config).await?,
Some(lockfile) => lockfile.into(),
};
let rockspec = project.new_local_rockspec();
let lua_version = rockspec.lua_version_from_config(&config)?;
let tree = project.tree(lua_version)?;

// Ensure all dependencies are installed first
let dependencies = rockspec
Expand All @@ -110,18 +59,24 @@ pub async fn build(data: Build, config: Config) -> Result<()> {
tree.match_rocks(req)
.is_ok_and(|rock_match| rock_match.is_found())
})
.map(|dep| (build_behaviour, dep.to_owned()));
.map(|dep| (BuildBehaviour::NoForce, dep.to_owned()));

Install::new(&config)
.packages(dependencies_to_install)
.pin(pin)
.package_db(package_db)
.progress(progress_arc)
.install()
.await?;

if !data.no_lock {
std::fs::copy(tree.lockfile_path(), project.lockfile_path())
.wrap_err("error copying the project lockfile")?;
}

build::Build::new(&rockspec, &config, &progress.map(|p| p.new_bar()))
.pin(pin)
.behaviour(build_behaviour)
.behaviour(BuildBehaviour::Force)
.build()
.await?;

Expand Down
84 changes: 84 additions & 0 deletions rocks-bin/src/install_rockspec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use eyre::{eyre, Context};
use itertools::Itertools;
use std::{path::PathBuf, sync::Arc};

use clap::Args;
use eyre::Result;
use rocks_lib::{
build::{self, BuildBehaviour},
config::Config,
lockfile::PinnedState,
operations::Install,
package::PackageName,
progress::MultiProgress,
project::Project,
rockspec::Rockspec,
tree::Tree,
};

#[derive(Args, Default)]
pub struct InstallRockspec {
/// The path to the RockSpec file to install
rockspec_path: PathBuf,

/// Whether to pin the installed package and dependencies.
#[arg(long)]
pin: bool,
}

pub async fn install_rockspec(data: InstallRockspec, config: Config) -> Result<()> {
let pin = PinnedState::from(data.pin);
let project_opt = Project::current()?;
let path = data.rockspec_path;

if path
.extension()
.map(|ext| ext != "rockspec")
.unwrap_or(true)
{
return Err(eyre!("Provided path is not a valid rockspec!"));
}
let content = std::fs::read_to_string(path)?;
let rockspec = Rockspec::new(&content)?;
let lua_version = rockspec.lua_version_from_config(&config)?;
let tree = Tree::new(config.tree().clone(), lua_version)?;

// Ensure all dependencies are installed first
let dependencies = rockspec
.dependencies
.current_platform()
.iter()
.filter(|package| !package.name().eq(&PackageName::new("lua".into())))
.collect_vec();

let progress_arc = MultiProgress::new_arc();
let progress = Arc::clone(&progress_arc);

let dependencies_to_install = dependencies
.into_iter()
.filter(|req| {
tree.match_rocks(req)
.is_ok_and(|rock_match| rock_match.is_found())
})
.map(|dep| (BuildBehaviour::NoForce, dep.to_owned()));

Install::new(&config)
.packages(dependencies_to_install)
.pin(pin)
.progress(progress_arc)
.install()
.await?;

if let Some(project) = project_opt {
std::fs::copy(tree.lockfile_path(), project.lockfile_path())
.wrap_err("error creating project lockfile.")?;
}

build::Build::new(&rockspec, &config, &progress.map(|p| p.new_bar()))
.pin(pin)
.behaviour(BuildBehaviour::Force)
.build()
.await?;

Ok(())
}
7 changes: 6 additions & 1 deletion rocks-bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use debug::Debug;
use download::Download;
use info::Info;
use install::Install;
use install_rockspec::InstallRockspec;
use list::ListCmd;
use outdated::Outdated;
use path::Path;
Expand All @@ -30,6 +31,7 @@ pub mod format;
pub mod info;
pub mod install;
pub mod install_lua;
pub mod install_rockspec;
pub mod list;
pub mod outdated;
pub mod path;
Expand Down Expand Up @@ -110,7 +112,7 @@ pub struct Cli {
pub enum Commands {
/// [UNIMPLEMENTED] Add a dependency to the current project.
Add,
/// Build/compile a rock.
/// Build/compile a project.
Build(Build),
/// Runs `luacheck` in the current project.
Check,
Expand All @@ -131,6 +133,9 @@ pub enum Commands {
/// Install a rock for use on the system.
#[command(arg_required_else_help = true)]
Install(Install),
/// Install a local RockSpec for use on the system.
#[command(arg_required_else_help = true)]
InstallRockspec(InstallRockspec),
/// Manually install and manage Lua headers for various Lua versions.
InstallLua,
/// [UNIMPLEMENTED] Check syntax of a rockspec.
Expand Down
9 changes: 7 additions & 2 deletions rocks-bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use clap::Parser;
use rocks::{
build, check,
debug::Debug,
download, fetch, format, info, install, install_lua, list, outdated, path, pin, project, purge,
remove, run, run_lua, search, test, unpack, update,
download, fetch, format, info, install, install_lua, install_rockspec, list, outdated, path,
pin, project, purge, remove, run, run_lua, search, test, unpack, update,
upload::{self},
Cli, Commands,
};
Expand Down Expand Up @@ -56,6 +56,11 @@ async fn main() {
Commands::List(list_data) => list::list_installed(list_data, config).unwrap(),
Commands::Lua(run_lua) => run_lua::run_lua(run_lua, config).await.unwrap(),
Commands::Install(install_data) => install::install(install_data, config).await.unwrap(),
Commands::InstallRockspec(install_data) => {
install_rockspec::install_rockspec(install_data, config)
.await
.unwrap()
}
Commands::Outdated(outdated) => outdated::outdated(outdated, config).await.unwrap(),
Commands::InstallLua => install_lua::install_lua(config).await.unwrap(),
Commands::Fmt => format::format().unwrap(),
Expand Down
Loading

0 comments on commit 67a1a1f

Please sign in to comment.