Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!(rocks-bin/build): project lockfiles #308

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 29 additions & 74 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 @@ -108,20 +57,26 @@ pub async fn build(data: Build, config: Config) -> Result<()> {
.into_iter()
.filter(|req| {
tree.match_rocks(req)
.is_ok_and(|rock_match| rock_match.is_found())
.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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A sample project where the lockfile does not have all test dependencies.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return true
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
rockspec_format = "3.0"
package = "foo"
version = "1.0.0-1"

dependencies = {
"lua >= 5.1",
"pathlib.nvim == 2.2.3",
-- "rtp.nvim == 1.2.0",
}

source = {
url = 'https://github.com/nvim-neorocks/luarocks-stub',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"version": "1.0.0",
"rocks": {
"5f37e8dc4251f549b25ec62138484f4912ae3d332e57bdb1b30184db3f59d270": {
"name": "pathlib.nvim",
"version": "2.2.3-1",
"pinned": false,
"dependencies": [
"d7142f441ca59ebde5927d8a3489966828c6b6655125616485632a1b16362f8e"
],
"constraint": "=2.2.3",
"source": "luarocks_rockspec+https://luarocks.org/",
"hashes": {
"rockspec": "sha256-kdDMqznWlwP9wIqlzPrZ5qEDp6edhlkaasAcQzWTmmM=",
"source": "sha256-fNO24tL8wApI8j3rk2mdLf5wbbjlUzsvCxki3n0xRw8="
}
},
"d7142f441ca59ebde5927d8a3489966828c6b6655125616485632a1b16362f8e": {
"name": "nvim-nio",
"version": "1.10.0-1",
"pinned": false,
"dependencies": [],
"constraint": ">=1.8.0",
"source": "luarocks_rockspec+https://luarocks.org/",
"hashes": {
"rockspec": "sha256-5iL9++6X/JsKKtpIRaRHcnZpn6DrsAQQRWPubZK9erY=",
"source": "sha256-wbN0gqfxaFgVcFc8EQl2Y8Cccd7jIPtdTMhHRQe5MSU="
}
}
},
"entrypoints": [
"5f37e8dc4251f549b25ec62138484f4912ae3d332e57bdb1b30184db3f59d270"
]
}
4 changes: 2 additions & 2 deletions rocks-lib/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ async fn do_build(build: Build<'_>) -> Result<LocalPackage, BuildError> {

let temp_dir = tempdir::TempDir::new(&build.rockspec.package.to_string())?;

operations::FetchSrc::new(
let source_hash = operations::FetchSrc::new(
temp_dir.path(),
build.rockspec,
build.config,
Expand All @@ -253,7 +253,7 @@ async fn do_build(build: Build<'_>) -> Result<LocalPackage, BuildError> {

let hashes = LocalPackageHashes {
rockspec: build.rockspec.hash()?,
source: temp_dir.hash()?,
source: source_hash,
};

if let Some(expected) = &build.rockspec.source.current_platform().integrity {
Expand Down
Loading
Loading