feat: remove more data redundancy from lockfiles

This commit is contained in:
daimond113 2025-01-01 17:16:41 +01:00
parent 78e58d63fa
commit 83fa22f7de
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
13 changed files with 155 additions and 102 deletions

View file

@ -12,8 +12,9 @@ use pesde::{
download_and_link::DownloadAndLinkOptions,
linking::generator::generate_bin_linking_module,
manifest::target::TargetKind,
names::PackageName,
names::{PackageName, PackageNames},
source::{
ids::PackageId,
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
traits::{
DownloadOptions, GetTargetOptions, PackageSource, RefreshOptions, ResolveOptions,
@ -81,7 +82,7 @@ impl ExecuteCommand {
.context("failed to refresh source")?;
let version_req = self.package.1.unwrap_or(VersionReq::STAR);
let Some((version, pkg_ref)) = ('finder: {
let Some((id, pkg_ref)) = ('finder: {
let specifier = PesdeDependencySpecifier {
name: self.package.0.clone(),
version: version_req.clone(),
@ -89,7 +90,7 @@ impl ExecuteCommand {
target: None,
};
if let Some(res) = source
if let Some((v_id, pkg_ref)) = source
.resolve(
&specifier,
&ResolveOptions {
@ -103,7 +104,10 @@ impl ExecuteCommand {
.1
.pop_last()
{
break 'finder Some(res);
break 'finder Some((
PackageId::new(PackageNames::Pesde(self.package.0.clone()), v_id),
pkg_ref,
));
}
source
@ -119,6 +123,12 @@ impl ExecuteCommand {
.context("failed to resolve package")?
.1
.pop_last()
.map(|(v_id, pkg_ref)| {
(
PackageId::new(PackageNames::Pesde(self.package.0.clone()), v_id),
pkg_ref,
)
})
}) else {
anyhow::bail!(
"no Lune or Luau package could be found for {}@{version_req}",
@ -141,6 +151,8 @@ impl ExecuteCommand {
project.auth_config().clone(),
);
let id = Arc::new(id);
let fs = source
.download(
&pkg_ref,
@ -148,6 +160,7 @@ impl ExecuteCommand {
project: project.clone(),
reqwest: reqwest.clone(),
reporter: Arc::new(()),
id: id.clone(),
},
)
.await
@ -163,6 +176,7 @@ impl ExecuteCommand {
&GetTargetOptions {
project: project.clone(),
path: Arc::from(tempdir.path()),
id: id.clone(),
},
)
.await
@ -176,10 +190,7 @@ impl ExecuteCommand {
.context("failed to build dependency graph")?;
multi_progress.suspend(|| {
eprintln!(
"{}",
format!("using {}", format!("{}@{version}", pkg_ref.name).bold()).dimmed()
)
eprintln!("{}", format!("using {}", format!("{id}").bold()).dimmed())
});
root_progress.reset();

View file

@ -6,18 +6,19 @@ use inquire::validator::Validation;
use pesde::{
errors::ManifestReadError,
manifest::{target::TargetKind, DependencyType},
names::PackageName,
names::{PackageName, PackageNames},
source::{
git_index::GitBasedSource,
ids::PackageId,
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
specifiers::DependencySpecifiers,
traits::{PackageSource, RefreshOptions, ResolveOptions},
traits::{GetTargetOptions, PackageSource, RefreshOptions, ResolveOptions},
PackageSources,
},
Project, RefreshedSources, DEFAULT_INDEX_NAME, SCRIPTS_LINK_FOLDER,
};
use semver::VersionReq;
use std::{fmt::Display, str::FromStr};
use std::{fmt::Display, path::Path, str::FromStr, sync::Arc};
#[derive(Debug, Args)]
pub struct InitCommand {}
@ -197,7 +198,7 @@ impl InitCommand {
let (v_id, pkg_ref) = source
.resolve(
&PesdeDependencySpecifier {
name: scripts_pkg_name,
name: scripts_pkg_name.clone(),
version: VersionReq::STAR,
index: None,
target: None,
@ -214,8 +215,22 @@ impl InitCommand {
.pop_last()
.context("scripts package not found")?;
let Some(scripts) = pkg_ref.target.scripts().filter(|s| !s.is_empty()) else {
anyhow::bail!("scripts package has no scripts. this is an issue with the index")
let id = Arc::new(PackageId::new(PackageNames::Pesde(scripts_pkg_name), v_id));
let target = source
.get_target(
&pkg_ref,
&GetTargetOptions {
project: project.clone(),
// HACK: the pesde package source doesn't use the path, so we can just use an empty one
path: Arc::from(Path::new("")),
id: id.clone(),
},
)
.await?;
let Some(scripts) = target.scripts().filter(|s| !s.is_empty()) else {
anyhow::bail!("scripts package has no scripts.")
};
let scripts_field = &mut manifest["scripts"]
@ -231,9 +246,9 @@ impl InitCommand {
.or_insert(toml_edit::Item::Table(toml_edit::Table::new()));
let field = &mut dev_deps["scripts"];
field["name"] = toml_edit::value(pkg_ref.name.to_string());
field["version"] = toml_edit::value(format!("^{}", v_id.version()));
field["target"] = toml_edit::value(v_id.target().to_string());
field["name"] = toml_edit::value(id.name().to_string());
field["version"] = toml_edit::value(format!("^{}", id.version_id().version()));
field["target"] = toml_edit::value(id.version_id().target().to_string());
for (alias, (spec, ty)) in pkg_ref.dependencies {
if ty != DependencyType::Peer {
@ -247,8 +262,11 @@ impl InitCommand {
let field = &mut dev_deps[alias];
field["name"] = toml_edit::value(spec.name.to_string());
field["version"] = toml_edit::value(spec.version.to_string());
field["target"] =
toml_edit::value(spec.target.unwrap_or_else(|| *v_id.target()).to_string());
field["target"] = toml_edit::value(
spec.target
.unwrap_or_else(|| *id.version_id().target())
.to_string(),
);
}
} else {
println!(

View file

@ -57,6 +57,7 @@ impl PatchCommand {
project: project.clone(),
reqwest,
reporter: Arc::new(()),
id: Arc::new(id),
},
)
.await?

View file

@ -131,6 +131,7 @@ impl PublishCommand {
.join(PACKAGES_CONTAINER_NAME)
.join(node.container_folder(id));
let id = Arc::new(id.clone());
let node = node.clone();
let refreshed_sources = refreshed_sources.clone();
@ -151,6 +152,7 @@ impl PublishCommand {
&GetTargetOptions {
project,
path: Arc::from(container_folder),
id,
},
)
.await?;

View file

@ -111,6 +111,7 @@ impl RunCommand {
&GetTargetOptions {
project,
path: Arc::from(container_folder.as_path()),
id: Arc::new(id),
},
)
.await?;

View file

@ -146,7 +146,7 @@ impl Project {
let refreshed_sources = refreshed_sources.clone();
let package_dir = project.package_dir().to_path_buf();
let semaphore = semaphore.clone();
let package_id = package_id.clone();
let package_id = Arc::new(package_id.clone());
let node = node.clone();
async move {
@ -189,6 +189,7 @@ impl Project {
&DownloadOptions {
project: project.clone(),
reqwest,
id: package_id.clone(),
reporter: Arc::new(progress_reporter),
},
)
@ -201,6 +202,7 @@ impl Project {
&DownloadOptions {
project: project.clone(),
reqwest,
id: package_id.clone(),
reporter: Arc::new(()),
},
)
@ -225,6 +227,7 @@ impl Project {
&GetTargetOptions {
project,
path: Arc::from(container_folder),
id: package_id.clone(),
},
)
.await
@ -236,7 +239,7 @@ impl Project {
}
let downloaded_node = DownloadedDependencyGraphNode { node, target };
Ok((downloaded_node, package_id))
Ok((downloaded_node, Arc::into_inner(package_id).unwrap()))
}
.instrument(span)
})

View file

@ -50,7 +50,7 @@ impl DependencyGraphNode {
if self.pkg_ref.like_wally() {
return PathBuf::from(format!(
"{}_{}@{}",
package_id.name().as_str().0,
name.as_str().0,
name.as_str().1,
version
))

View file

@ -94,6 +94,25 @@ impl PesdePackageSource {
.await
.unwrap()
}
fn read_index_file(
&self,
name: &PackageName,
project: &Project,
) -> Result<Option<IndexFile>, errors::ReadIndexFileError> {
let (scope, name) = name.as_str();
let repo = gix::open(self.path(project)).map_err(Box::new)?;
let tree = root_tree(&repo).map_err(Box::new)?;
let string = match read_file(&tree, [scope, name]) {
Ok(Some(s)) => s,
Ok(None) => return Ok(None),
Err(e) => {
return Err(errors::ReadIndexFileError::ReadFile(e));
}
};
toml::from_str(&string).map_err(Into::into)
}
}
impl PackageSource for PesdePackageSource {
@ -121,23 +140,11 @@ impl PackageSource for PesdePackageSource {
..
} = options;
let (scope, name) = specifier.name.as_str();
let repo = gix::open(self.path(project)).map_err(Box::new)?;
let tree = root_tree(&repo).map_err(Box::new)?;
let string = match read_file(&tree, [scope, name]) {
Ok(Some(s)) => s,
Ok(None) => return Err(Self::ResolveError::NotFound(specifier.name.to_string())),
Err(e) => {
return Err(Self::ResolveError::Read(
specifier.name.to_string(),
Box::new(e),
))
}
let Some(IndexFile { entries, .. }) = self.read_index_file(&specifier.name, project)?
else {
return Err(errors::ResolveError::NotFound(specifier.name.to_string()));
};
let IndexFile { entries, .. } = toml::from_str(&string)
.map_err(|e| Self::ResolveError::Parse(specifier.name.to_string(), e))?;
tracing::debug!("{} has {} possible entries", specifier.name, entries.len());
Ok((
@ -149,16 +156,11 @@ impl PackageSource for PesdePackageSource {
&& specifier.target.unwrap_or(*project_target) == *target
})
.map(|(id, entry)| {
let version = id.version().clone();
(
id,
PesdePackageRef {
name: specifier.name.clone(),
version,
index_url: self.repo_url.clone(),
dependencies: entry.dependencies,
target: entry.target,
},
)
})
@ -169,31 +171,28 @@ impl PackageSource for PesdePackageSource {
#[instrument(skip_all, level = "debug")]
async fn download<R: DownloadProgressReporter>(
&self,
pkg_ref: &Self::Ref,
_pkg_ref: &Self::Ref,
options: &DownloadOptions<R>,
) -> Result<PackageFs, Self::DownloadError> {
let DownloadOptions {
project,
reporter,
reqwest,
id,
..
} = options;
let config = self.config(project).await.map_err(Box::new)?;
let index_file = project
.cas_dir()
.join("index")
.join(pkg_ref.name.escaped())
.join(pkg_ref.version.to_string())
.join(pkg_ref.target.to_string());
.join(id.name().escaped())
.join(id.version_id().version().to_string())
.join(id.version_id().target().to_string());
match fs::read_to_string(&index_file).await {
Ok(s) => {
tracing::debug!(
"using cached index file for package {}@{} {}",
pkg_ref.name,
pkg_ref.version,
pkg_ref.target
);
tracing::debug!("using cached index file for package {id}");
reporter.report_done();
@ -205,9 +204,9 @@ impl PackageSource for PesdePackageSource {
let url = config
.download()
.replace("{PACKAGE}", &pkg_ref.name.to_string().replace("/", "%2F"))
.replace("{PACKAGE_VERSION}", &pkg_ref.version.to_string())
.replace("{PACKAGE_TARGET}", &pkg_ref.target.to_string());
.replace("{PACKAGE}", &id.name().to_string().replace("/", "%2F"))
.replace("{PACKAGE_VERSION}", &id.version_id().version().to_string())
.replace("{PACKAGE_TARGET}", &id.version_id().target().to_string());
let mut request = reqwest.get(&url).header(ACCEPT, "application/octet-stream");
@ -294,10 +293,24 @@ impl PackageSource for PesdePackageSource {
#[instrument(skip_all, level = "debug")]
async fn get_target(
&self,
pkg_ref: &Self::Ref,
_options: &GetTargetOptions,
_pkg_ref: &Self::Ref,
options: &GetTargetOptions,
) -> Result<Target, Self::GetTargetError> {
Ok(pkg_ref.target.clone())
let GetTargetOptions { id, .. } = options;
let PackageNames::Pesde(name) = id.name() else {
panic!("unexpected package name");
};
let Some(IndexFile { mut entries, .. }) = self.read_index_file(name, &options.project)?
else {
return Err(errors::GetTargetError::NotFound(name.to_string()));
};
let entry = entries
.remove(id.version_id())
.ok_or_else(|| errors::GetTargetError::NotFound(name.to_string()))?;
Ok(entry.target)
}
}
@ -496,10 +509,14 @@ pub mod errors {
use crate::source::git_index::errors::{ReadFile, TreeError};
/// Errors that can occur when resolving a package from a pesde package source
/// Errors that can occur when reading an index file of a pesde package source
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ResolveError {
pub enum ReadIndexFileError {
/// Error reading file
#[error("error reading file")]
ReadFile(#[from] ReadFile),
/// Error opening repository
#[error("error opening repository")]
Open(#[from] Box<gix::open::Error>),
@ -508,17 +525,22 @@ pub mod errors {
#[error("error getting tree")]
Tree(#[from] Box<TreeError>),
/// Error parsing file
#[error("error parsing file")]
Parse(#[from] toml::de::Error),
}
/// Errors that can occur when resolving a package from a pesde package source
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ResolveError {
/// Package not found in index
#[error("package {0} not found")]
NotFound(String),
/// Error reading file for package
#[error("error reading file for {0}")]
Read(String, #[source] Box<ReadFile>),
/// Error parsing file for package
#[error("error parsing file for {0}")]
Parse(String, #[source] toml::de::Error),
/// Error reading index file
#[error("error reading index file")]
ReadIndex(#[from] ReadIndexFileError),
}
/// Errors that can occur when reading the config file for a pesde package source
@ -586,5 +608,13 @@ pub mod errors {
/// Errors that can occur when getting the target for a package from a pesde package source
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum GetTargetError {}
pub enum GetTargetError {
/// Error reading index file
#[error("error reading index file")]
ReadIndex(#[from] ReadIndexFileError),
/// Package not found in index
#[error("package `{0}` not found in index")]
NotFound(String),
}
}

View file

@ -1,21 +1,15 @@
use std::collections::BTreeMap;
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::{
manifest::{target::Target, DependencyType},
names::PackageName,
manifest::DependencyType,
source::{pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSources},
};
/// A pesde package reference
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub struct PesdePackageRef {
/// The name of the package
pub name: PackageName,
/// The version of the package
pub version: Version,
/// The index of the package
#[serde(
serialize_with = "crate::util::serialize_gix_url",
@ -25,8 +19,6 @@ pub struct PesdePackageRef {
/// The dependencies of the package
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
/// The target of the package
pub target: Target,
}
impl PackageRef for PesdePackageRef {
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> {

View file

@ -4,7 +4,7 @@ use crate::{
DependencyType,
},
reporters::DownloadProgressReporter,
source::{DependencySpecifiers, PackageFs, PackageSources, ResolveResult},
source::{ids::PackageId, DependencySpecifiers, PackageFs, PackageSources, ResolveResult},
Project, RefreshedSources,
};
use std::{
@ -55,6 +55,8 @@ pub struct DownloadOptions<R: DownloadProgressReporter> {
pub reqwest: reqwest::Client,
/// The reporter to use
pub reporter: Arc<R>,
/// The package ID of the package to be downloaded
pub id: Arc<PackageId>,
}
/// Options for getting a package's Target
@ -64,6 +66,8 @@ pub struct GetTargetOptions {
pub project: Project,
/// The path the package has been written to
pub path: Arc<Path>,
/// The package ID of the package to be downloaded
pub id: Arc<PackageId>,
}
/// A source of packages

View file

@ -60,7 +60,7 @@ pub(crate) const WALLY_MANIFEST_FILE_NAME: &str = "wally.toml";
pub(crate) async fn get_target(
options: &GetTargetOptions,
) -> Result<Target, errors::GetTargetError> {
let GetTargetOptions { project, path } = options;
let GetTargetOptions { project, path, .. } = options;
let lib = find_lib_path(project, path)
.await?

View file

@ -185,21 +185,21 @@ impl PackageSource for WallyPackageSource {
.into_iter()
.filter(|manifest| specifier.version.matches(&manifest.package.version))
.map(|manifest| {
let dependencies = manifest.all_dependencies().map_err(|e| {
errors::ResolveError::AllDependencies(specifier.to_string(), e)
})?;
Ok((
VersionId(
manifest.package.version.clone(),
manifest.package.version,
match manifest.package.realm {
Realm::Server => TargetKind::RobloxServer,
_ => TargetKind::Roblox,
},
),
WallyPackageRef {
name: specifier.name.clone(),
index_url: source.repo_url.clone(),
dependencies: manifest.all_dependencies().map_err(|e| {
errors::ResolveError::AllDependencies(specifier.to_string(), e)
})?,
version: manifest.package.version,
dependencies,
},
))
})
@ -213,29 +213,27 @@ impl PackageSource for WallyPackageSource {
#[instrument(skip_all, level = "debug")]
async fn download<R: DownloadProgressReporter>(
&self,
pkg_ref: &Self::Ref,
_pkg_ref: &Self::Ref,
options: &DownloadOptions<R>,
) -> Result<PackageFs, Self::DownloadError> {
let DownloadOptions {
project,
reqwest,
reporter,
id,
..
} = options;
let config = self.config(project).await.map_err(Box::new)?;
let index_file = project
.cas_dir()
.join("wally_index")
.join(pkg_ref.name.escaped())
.join(pkg_ref.version.to_string());
.join(id.name().escaped())
.join(id.version_id().version().to_string());
match fs::read_to_string(&index_file).await {
Ok(s) => {
tracing::debug!(
"using cached index file for package {}@{}",
pkg_ref.name,
pkg_ref.version
);
tracing::debug!("using cached index file for package {id}");
reporter.report_done();
@ -245,13 +243,13 @@ impl PackageSource for WallyPackageSource {
Err(e) => return Err(errors::DownloadError::ReadIndex(e)),
};
let (scope, name) = pkg_ref.name.as_str();
let (scope, name) = id.name().as_str();
let mut request = reqwest
.get(format!(
"{}/v1/package-contents/{scope}/{name}/{}",
config.api.as_str().trim_end_matches('/'),
pkg_ref.version
id.version_id().version()
))
.header(
"Wally-Version",

View file

@ -1,22 +1,15 @@
use std::collections::BTreeMap;
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::{
manifest::DependencyType,
names::wally::WallyPackageName,
source::{wally::WallyPackageSource, DependencySpecifiers, PackageRef, PackageSources},
};
/// A Wally package reference
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub struct WallyPackageRef {
/// The name of the package
#[serde(rename = "wally")]
pub name: WallyPackageName,
/// The version of the package
pub version: Version,
/// The index of the package
#[serde(
serialize_with = "crate::util::serialize_gix_url",