mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-04-05 11:20:55 +01:00
feat(engines): print incompatibility warning for dependencies
Adds a warning message when a dependency depends on an incompatible engine.
This commit is contained in:
parent
4946a19f8b
commit
2d534a534d
4 changed files with 164 additions and 42 deletions
|
@ -368,6 +368,7 @@ pub async fn publish_package(
|
|||
let new_entry = IndexFileEntry {
|
||||
target: manifest.target.clone(),
|
||||
published_at: chrono::Utc::now(),
|
||||
engines: manifest.engines.clone(),
|
||||
description: manifest.description.clone(),
|
||||
license: manifest.license.clone(),
|
||||
authors: manifest.authors.clone(),
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
use std::{
|
||||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
num::NonZeroUsize,
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use super::files::make_executable;
|
||||
use crate::cli::{
|
||||
bin_dir,
|
||||
|
@ -16,10 +9,19 @@ use colored::Colorize;
|
|||
use fs_err::tokio as fs;
|
||||
use pesde::{
|
||||
download_and_link::{DownloadAndLinkHooks, DownloadAndLinkOptions},
|
||||
engine::EngineKind,
|
||||
graph::{DependencyGraph, DependencyGraphWithTarget},
|
||||
lockfile::Lockfile,
|
||||
manifest::{target::TargetKind, DependencyType},
|
||||
Project, RefreshedSources, LOCKFILE_FILE_NAME, MANIFEST_FILE_NAME,
|
||||
manifest::{target::TargetKind, DependencyType, Manifest},
|
||||
names::PackageNames,
|
||||
source::{pesde::PesdePackageSource, refs::PackageRefs},
|
||||
version_matches, Project, RefreshedSources, LOCKFILE_FILE_NAME, MANIFEST_FILE_NAME,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
num::NonZeroUsize,
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
|
@ -196,7 +198,8 @@ pub async fn install(
|
|||
let overrides = resolve_overrides(&manifest)?;
|
||||
|
||||
let (new_lockfile, old_graph) =
|
||||
reporters::run_with_reporter(|_, root_progress, reporter| async {
|
||||
reporters::run_with_reporter(|multi, root_progress, reporter| async {
|
||||
let multi = multi;
|
||||
let root_progress = root_progress;
|
||||
|
||||
root_progress.set_prefix(format!("{} {}: ", manifest.name, manifest.target));
|
||||
|
@ -300,9 +303,104 @@ pub async fn install(
|
|||
root_progress.set_message("patch");
|
||||
|
||||
project
|
||||
.apply_patches(&downloaded_graph.convert(), reporter)
|
||||
.apply_patches(&downloaded_graph.clone().convert(), reporter)
|
||||
.await?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "version-management")]
|
||||
{
|
||||
let mut tasks = manifest
|
||||
.engines
|
||||
.into_iter()
|
||||
.map(|(engine, req)| async move {
|
||||
Ok::<_, anyhow::Error>(
|
||||
crate::cli::version::get_installed_versions(engine)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|version| version_matches(version, &req))
|
||||
.last()
|
||||
.map(|version| (engine, version)),
|
||||
)
|
||||
})
|
||||
.collect::<JoinSet<_>>();
|
||||
|
||||
let mut resolved_engine_versions = HashMap::new();
|
||||
while let Some(task) = tasks.join_next().await {
|
||||
let Some((engine, version)) = task.unwrap()? else {
|
||||
continue;
|
||||
};
|
||||
resolved_engine_versions.insert(engine, version);
|
||||
}
|
||||
|
||||
let manifest_target_kind = manifest.target.kind();
|
||||
let mut tasks = downloaded_graph.iter()
|
||||
.map(|(id, node)| {
|
||||
let id = id.clone();
|
||||
let node = node.clone();
|
||||
let project = project.clone();
|
||||
|
||||
async move {
|
||||
let engines = match &node.node.pkg_ref {
|
||||
PackageRefs::Pesde(pkg_ref) => {
|
||||
let source = PesdePackageSource::new(pkg_ref.index_url.clone());
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
let PackageNames::Pesde(name) = id.name() else {
|
||||
panic!("unexpected package name");
|
||||
};
|
||||
|
||||
let mut file = source.read_index_file(name, &project).await.context("failed to read package index file")?.context("package not found in index")?;
|
||||
file
|
||||
.entries
|
||||
.remove(id.version_id())
|
||||
.context("package version not found in index")?
|
||||
.engines
|
||||
}
|
||||
#[cfg(feature = "wally-compat")]
|
||||
PackageRefs::Wally(_) => Default::default(),
|
||||
_ => {
|
||||
let path = node.node.container_folder_from_project(
|
||||
&id,
|
||||
&project,
|
||||
manifest_target_kind,
|
||||
);
|
||||
|
||||
match fs::read_to_string(path.join(MANIFEST_FILE_NAME)).await {
|
||||
Ok(manifest) => match toml::from_str::<Manifest>(&manifest) {
|
||||
Ok(manifest) => manifest.engines,
|
||||
Err(e) => return Err(e).context("failed to read package manifest"),
|
||||
},
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Default::default(),
|
||||
Err(e) => return Err(e).context("failed to read package manifest"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok((id, engines))
|
||||
}
|
||||
})
|
||||
.collect::<JoinSet<_>>();
|
||||
|
||||
while let Some(task) = tasks.join_next().await {
|
||||
let (id, required_engines) = task.unwrap()?;
|
||||
|
||||
for (engine, req) in required_engines {
|
||||
if engine == EngineKind::Pesde {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(version) = resolved_engine_versions.get(&engine) else {
|
||||
tracing::debug!("package {id} requires {engine} {req}, but it is not installed");
|
||||
continue;
|
||||
};
|
||||
|
||||
if !version_matches(version, &req) {
|
||||
multi.suspend(|| {
|
||||
println!("{}: package {id} requires {engine} {req}, but {version} is installed", "warn".yellow().bold());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root_progress.set_message("finish");
|
||||
|
|
|
@ -142,25 +142,20 @@ pub async fn check_for_updates(reqwest: &reqwest::Client) -> anyhow::Result<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(reqwest), level = "trace")]
|
||||
pub async fn get_or_download_engine(
|
||||
reqwest: &reqwest::Client,
|
||||
engine: EngineKind,
|
||||
req: VersionReq,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
const ENGINES_DIR: &str = "engines";
|
||||
|
||||
#[instrument(level = "trace")]
|
||||
pub async fn get_installed_versions(engine: EngineKind) -> anyhow::Result<BTreeSet<Version>> {
|
||||
let source = engine.source();
|
||||
|
||||
let path = home_dir()?.join("engines").join(source.directory());
|
||||
fs::create_dir_all(&path)
|
||||
.await
|
||||
.context("failed to create engines directory")?;
|
||||
|
||||
let mut read_dir = fs::read_dir(&path)
|
||||
.await
|
||||
.context("failed to read engines directory")?;
|
||||
|
||||
let path = home_dir()?.join(ENGINES_DIR).join(source.directory());
|
||||
let mut installed_versions = BTreeSet::new();
|
||||
|
||||
let mut read_dir = match fs::read_dir(&path).await {
|
||||
Ok(read_dir) => read_dir,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(installed_versions),
|
||||
Err(e) => return Err(e).context("failed to read engines directory"),
|
||||
};
|
||||
|
||||
while let Some(entry) = read_dir.next_entry().await? {
|
||||
let path = entry.path();
|
||||
|
||||
|
@ -173,6 +168,20 @@ pub async fn get_or_download_engine(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(installed_versions)
|
||||
}
|
||||
|
||||
#[instrument(skip(reqwest), level = "trace")]
|
||||
pub async fn get_or_download_engine(
|
||||
reqwest: &reqwest::Client,
|
||||
engine: EngineKind,
|
||||
req: VersionReq,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
let source = engine.source();
|
||||
let path = home_dir()?.join(ENGINES_DIR).join(source.directory());
|
||||
|
||||
let installed_versions = get_installed_versions(engine).await?;
|
||||
|
||||
let max_matching = installed_versions
|
||||
.iter()
|
||||
.filter(|v| version_matches(v, &req))
|
||||
|
|
|
@ -13,6 +13,7 @@ use pkg_ref::PesdePackageRef;
|
|||
use specifier::PesdeDependencySpecifier;
|
||||
|
||||
use crate::{
|
||||
engine::EngineKind,
|
||||
manifest::{target::Target, DependencyType},
|
||||
names::{PackageName, PackageNames},
|
||||
reporters::{response_to_async_read, DownloadProgressReporter},
|
||||
|
@ -27,6 +28,7 @@ use crate::{
|
|||
};
|
||||
use fs_err::tokio as fs;
|
||||
use futures::StreamExt;
|
||||
use semver::VersionReq;
|
||||
use tokio::{pin, task::spawn_blocking};
|
||||
use tracing::instrument;
|
||||
|
||||
|
@ -94,13 +96,18 @@ impl PesdePackageSource {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn read_index_file(
|
||||
/// Reads the index file of a package
|
||||
pub async fn read_index_file(
|
||||
&self,
|
||||
name: &PackageName,
|
||||
project: &Project,
|
||||
) -> Result<Option<IndexFile>, errors::ReadIndexFileError> {
|
||||
let path = self.path(project);
|
||||
let name = name.clone();
|
||||
|
||||
spawn_blocking(move || {
|
||||
let (scope, name) = name.as_str();
|
||||
let repo = gix::open(self.path(project)).map_err(Box::new)?;
|
||||
let repo = gix::open(&path).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,
|
||||
|
@ -111,6 +118,9 @@ impl PesdePackageSource {
|
|||
};
|
||||
|
||||
toml::from_str(&string).map_err(Into::into)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +150,7 @@ impl PackageSource for PesdePackageSource {
|
|||
} = options;
|
||||
|
||||
let Some(IndexFile { meta, entries, .. }) =
|
||||
self.read_index_file(&specifier.name, project)?
|
||||
self.read_index_file(&specifier.name, project).await?
|
||||
else {
|
||||
return Err(errors::ResolveError::NotFound(specifier.name.to_string()));
|
||||
};
|
||||
|
@ -296,7 +306,8 @@ impl PackageSource for PesdePackageSource {
|
|||
panic!("unexpected package name");
|
||||
};
|
||||
|
||||
let Some(IndexFile { mut entries, .. }) = self.read_index_file(name, &options.project)?
|
||||
let Some(IndexFile { mut entries, .. }) =
|
||||
self.read_index_file(name, &options.project).await?
|
||||
else {
|
||||
return Err(errors::GetTargetError::NotFound(name.to_string()));
|
||||
};
|
||||
|
@ -460,6 +471,9 @@ pub struct IndexFileEntry {
|
|||
/// When this package was published
|
||||
#[serde(default = "chrono::Utc::now")]
|
||||
pub published_at: chrono::DateTime<chrono::Utc>,
|
||||
/// The engines this package supports
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub engines: BTreeMap<EngineKind, VersionReq>,
|
||||
|
||||
/// The description of this package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
Loading…
Add table
Reference in a new issue