mirror of
https://github.com/mii443/wasmer.git
synced 2025-09-02 15:39:32 +00:00
feat: Partial support for unnamed packages
* Change PackageId to support both named and unnamed package * Update the package resolver logic with unnamed package support * Update the "package download" command
This commit is contained in:
@ -21,7 +21,7 @@ fn data_and_store_mut() -> Result<(), String> {
|
|||||||
);
|
);
|
||||||
let mut envmut = env.into_mut(&mut store);
|
let mut envmut = env.into_mut(&mut store);
|
||||||
|
|
||||||
let (mut data, mut storemut) = envmut.data_and_store_mut();
|
let (data, mut storemut) = envmut.data_and_store_mut();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.global.ty(&storemut),
|
data.global.ty(&storemut),
|
||||||
|
@ -92,6 +92,7 @@ mod queries {
|
|||||||
pub updated_at: DateTime,
|
pub updated_at: DateTime,
|
||||||
pub tag: String,
|
pub tag: String,
|
||||||
pub is_archived: bool,
|
pub is_archived: bool,
|
||||||
|
pub webc_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(cynic::QueryFragment, Debug, Clone, Serialize)]
|
#[derive(cynic::QueryFragment, Debug, Clone, Serialize)]
|
||||||
|
@ -93,18 +93,48 @@ impl PackageDownload {
|
|||||||
|
|
||||||
step_num += 1;
|
step_num += 1;
|
||||||
|
|
||||||
let (full_name, version, api_endpoint, token) = match &self.package {
|
let (download_url, token) = match &self.package {
|
||||||
PackageSpecifier::Registry { full_name, version } => {
|
PackageSpecifier::Registry { full_name, version } => {
|
||||||
let endpoint = self.env.registry_endpoint()?;
|
let endpoint = self.env.registry_endpoint()?;
|
||||||
let version = version.to_string();
|
let version = version.to_string();
|
||||||
let version = if version == "*" { None } else { Some(version) };
|
let version = if version == "*" { None } else { Some(version) };
|
||||||
|
let token = self.env.get_token_opt().map(|x| x.to_string());
|
||||||
|
|
||||||
(
|
let package = wasmer_registry::query_package_from_registry(
|
||||||
full_name,
|
endpoint.as_str(),
|
||||||
version,
|
&full_name,
|
||||||
endpoint,
|
version.as_deref(),
|
||||||
self.env.get_token_opt().map(|x| x.to_string()),
|
token.as_deref(),
|
||||||
)
|
)
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"could not retrieve package information for package '{}' from registry '{}'",
|
||||||
|
full_name, endpoint,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let download_url = package
|
||||||
|
.pirita_url
|
||||||
|
.context("registry does provide a container download container download URL")?;
|
||||||
|
|
||||||
|
(download_url, token)
|
||||||
|
}
|
||||||
|
PackageSpecifier::HashSha256(hash) => {
|
||||||
|
let endpoint = self.env.registry_endpoint()?;
|
||||||
|
let token = self.env.get_token_opt().map(|x| x.to_string());
|
||||||
|
|
||||||
|
let client = wasmer_api::WasmerClient::new(endpoint, "wasmer-cli")?;
|
||||||
|
let client = if let Some(token) = &token {
|
||||||
|
client.with_auth_token(token.clone())
|
||||||
|
} else {
|
||||||
|
client
|
||||||
|
};
|
||||||
|
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
let pkg = rt.block_on(wasmer_api::query::get_package_release(&client, &hash))?
|
||||||
|
.with_context(|| format!("Package with sha256:{hash} does not exist in the registry, or is not accessible"))?;
|
||||||
|
|
||||||
|
(pkg.webc_url, token)
|
||||||
}
|
}
|
||||||
PackageSpecifier::Url(url) => {
|
PackageSpecifier::Url(url) => {
|
||||||
bail!("cannot download a package from a URL: '{}'", url);
|
bail!("cannot download a package from a URL: '{}'", url);
|
||||||
@ -114,23 +144,6 @@ impl PackageDownload {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let package = wasmer_registry::query_package_from_registry(
|
|
||||||
api_endpoint.as_str(),
|
|
||||||
full_name,
|
|
||||||
version.as_deref(),
|
|
||||||
token.as_deref(),
|
|
||||||
)
|
|
||||||
.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"could not retrieve package information for package '{}' from registry '{}'",
|
|
||||||
full_name, api_endpoint,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let download_url = package
|
|
||||||
.pirita_url
|
|
||||||
.context("registry does provide a container download container download URL")?;
|
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let mut b = client
|
let mut b = client
|
||||||
.get(download_url)
|
.get(download_url)
|
||||||
|
@ -3,14 +3,13 @@ use std::sync::Arc;
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use semver::Version;
|
|
||||||
use virtual_fs::FileSystem;
|
use virtual_fs::FileSystem;
|
||||||
use webc::{compat::SharedBytes, Container};
|
use webc::{compat::SharedBytes, Container};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
runtime::{
|
runtime::{
|
||||||
module_cache::ModuleHash,
|
module_cache::ModuleHash,
|
||||||
resolver::{PackageId, PackageInfo, PackageSpecifier, ResolveError},
|
resolver::{PackageId, PackageIdent, PackageInfo, PackageSpecifier, ResolveError},
|
||||||
},
|
},
|
||||||
Runtime,
|
Runtime,
|
||||||
};
|
};
|
||||||
@ -60,7 +59,8 @@ impl BinaryPackageCommand {
|
|||||||
#[derive(Derivative, Clone)]
|
#[derive(Derivative, Clone)]
|
||||||
#[derivative(Debug)]
|
#[derivative(Debug)]
|
||||||
pub struct BinaryPackage {
|
pub struct BinaryPackage {
|
||||||
pub package_name: String,
|
pub id: PackageId,
|
||||||
|
|
||||||
pub when_cached: Option<u128>,
|
pub when_cached: Option<u128>,
|
||||||
/// The name of the [`BinaryPackageCommand`] which is this package's
|
/// The name of the [`BinaryPackageCommand`] which is this package's
|
||||||
/// entrypoint.
|
/// entrypoint.
|
||||||
@ -69,7 +69,6 @@ pub struct BinaryPackage {
|
|||||||
pub webc_fs: Arc<dyn FileSystem + Send + Sync>,
|
pub webc_fs: Arc<dyn FileSystem + Send + Sync>,
|
||||||
pub commands: Vec<BinaryPackageCommand>,
|
pub commands: Vec<BinaryPackageCommand>,
|
||||||
pub uses: Vec<String>,
|
pub uses: Vec<String>,
|
||||||
pub version: Version,
|
|
||||||
pub file_system_memory_footprint: u64,
|
pub file_system_memory_footprint: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +82,10 @@ impl BinaryPackage {
|
|||||||
) -> Result<Self, anyhow::Error> {
|
) -> Result<Self, anyhow::Error> {
|
||||||
let source = rt.source();
|
let source = rt.source();
|
||||||
let root = PackageInfo::from_manifest(container.manifest())?;
|
let root = PackageInfo::from_manifest(container.manifest())?;
|
||||||
let root_id = PackageId {
|
let root_id = PackageId::Named(PackageIdent {
|
||||||
package_name: root.name.clone(),
|
name: root.name.clone(),
|
||||||
version: root.version.clone(),
|
version: root.version.clone(),
|
||||||
};
|
});
|
||||||
|
|
||||||
let resolution = crate::runtime::resolver::resolve(&root_id, &root, &*source).await?;
|
let resolution = crate::runtime::resolver::resolve(&root_id, &root, &*source).await?;
|
||||||
let pkg = rt
|
let pkg = rt
|
||||||
@ -145,7 +144,7 @@ impl BinaryPackage {
|
|||||||
if let Some(entry) = self.entrypoint_bytes() {
|
if let Some(entry) = self.entrypoint_bytes() {
|
||||||
ModuleHash::hash(entry)
|
ModuleHash::hash(entry)
|
||||||
} else {
|
} else {
|
||||||
ModuleHash::hash(self.package_name.as_bytes())
|
ModuleHash::hash(self.id.to_string())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use wasmer_wasix_types::wasi::Errno;
|
|||||||
use super::{BinFactory, BinaryPackage};
|
use super::{BinFactory, BinaryPackage};
|
||||||
use crate::{Runtime, WasiEnv, WasiFunctionEnv};
|
use crate::{Runtime, WasiEnv, WasiFunctionEnv};
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all, fields(%name, %binary.package_name))]
|
#[tracing::instrument(level = "trace", skip_all, fields(%name, package_id=%binary.id))]
|
||||||
pub async fn spawn_exec(
|
pub async fn spawn_exec(
|
||||||
binary: BinaryPackage,
|
binary: BinaryPackage,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -37,8 +37,7 @@ pub async fn spawn_exec(
|
|||||||
} else {
|
} else {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
command=name,
|
command=name,
|
||||||
pkg.name=%binary.package_name,
|
pkg=%binary.id,
|
||||||
pkg.version=%binary.version,
|
|
||||||
"Unable to spawn a command because its package has no entrypoint",
|
"Unable to spawn a command because its package has no entrypoint",
|
||||||
);
|
);
|
||||||
env.on_exit(Some(Errno::Noexec.into())).await;
|
env.on_exit(Some(Errno::Noexec.into())).await;
|
||||||
|
@ -17,6 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
net::socket::InodeSocketKind,
|
net::socket::InodeSocketKind,
|
||||||
|
runtime::resolver::PackageId,
|
||||||
state::{Stderr, Stdin, Stdout},
|
state::{Stderr, Stdin, Stdout},
|
||||||
};
|
};
|
||||||
use futures::{future::BoxFuture, Future, TryStreamExt};
|
use futures::{future::BoxFuture, Future, TryStreamExt};
|
||||||
@ -492,7 +493,7 @@ pub struct WasiFs {
|
|||||||
#[cfg_attr(feature = "enable-serde", serde(skip, default))]
|
#[cfg_attr(feature = "enable-serde", serde(skip, default))]
|
||||||
pub root_fs: WasiFsRoot,
|
pub root_fs: WasiFsRoot,
|
||||||
pub root_inode: InodeGuard,
|
pub root_inode: InodeGuard,
|
||||||
pub has_unioned: Arc<Mutex<HashSet<String>>>,
|
pub has_unioned: Arc<Mutex<HashSet<PackageId>>>,
|
||||||
|
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
// using an atomic is a hack to enable customization after construction,
|
// using an atomic is a hack to enable customization after construction,
|
||||||
@ -565,9 +566,7 @@ impl WasiFs {
|
|||||||
&self,
|
&self,
|
||||||
binary: &BinaryPackage,
|
binary: &BinaryPackage,
|
||||||
) -> Result<(), virtual_fs::FsError> {
|
) -> Result<(), virtual_fs::FsError> {
|
||||||
let package_name = binary.package_name.clone();
|
let needs_to_be_unioned = self.has_unioned.lock().unwrap().insert(binary.id.clone());
|
||||||
|
|
||||||
let needs_to_be_unioned = self.has_unioned.lock().unwrap().insert(package_name);
|
|
||||||
|
|
||||||
if !needs_to_be_unioned {
|
if !needs_to_be_unioned {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -47,8 +47,7 @@ pub async fn load_package_tree(
|
|||||||
let file_system_memory_footprint = count_file_system(&fs, Path::new("/"));
|
let file_system_memory_footprint = count_file_system(&fs, Path::new("/"));
|
||||||
|
|
||||||
let loaded = BinaryPackage {
|
let loaded = BinaryPackage {
|
||||||
package_name: root.package_name.clone(),
|
id: root.clone(),
|
||||||
version: root.version.clone(),
|
|
||||||
when_cached: crate::syscalls::platform_clock_time_get(
|
when_cached: crate::syscalls::platform_clock_time_get(
|
||||||
wasmer_wasix_types::wasi::Snapshot0Clockid::Monotonic,
|
wasmer_wasix_types::wasi::Snapshot0Clockid::Monotonic,
|
||||||
1_000_000,
|
1_000_000,
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, VecDeque},
|
collections::{BTreeMap, HashMap, VecDeque},
|
||||||
fs::File,
|
fs::File,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use semver::Version;
|
|
||||||
|
|
||||||
use crate::runtime::resolver::{PackageSpecifier, PackageSummary, QueryError, Source};
|
use crate::runtime::resolver::{PackageSpecifier, PackageSummary, QueryError, Source};
|
||||||
|
|
||||||
|
use super::PackageId;
|
||||||
|
|
||||||
/// A [`Source`] that tracks packages in memory.
|
/// A [`Source`] that tracks packages in memory.
|
||||||
///
|
///
|
||||||
/// Primarily used during testing.
|
/// Primarily used during testing.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct InMemorySource {
|
pub struct InMemorySource {
|
||||||
packages: BTreeMap<String, Vec<PackageSummary>>,
|
named_packages: BTreeMap<String, Vec<PackageSummary>>,
|
||||||
|
hash_packages: HashMap<String, PackageSummary>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InMemorySource {
|
impl InMemorySource {
|
||||||
@ -62,7 +64,10 @@ impl InMemorySource {
|
|||||||
|
|
||||||
/// Add a new [`PackageSummary`] to the [`InMemorySource`].
|
/// Add a new [`PackageSummary`] to the [`InMemorySource`].
|
||||||
pub fn add(&mut self, summary: PackageSummary) {
|
pub fn add(&mut self, summary: PackageSummary) {
|
||||||
let summaries = self.packages.entry(summary.pkg.name.clone()).or_default();
|
let summaries = self
|
||||||
|
.named_packages
|
||||||
|
.entry(summary.pkg.name.clone())
|
||||||
|
.or_default();
|
||||||
summaries.push(summary);
|
summaries.push(summary);
|
||||||
summaries.sort_by(|left, right| left.pkg.version.cmp(&right.pkg.version));
|
summaries.sort_by(|left, right| left.pkg.version.cmp(&right.pkg.version));
|
||||||
summaries.dedup_by(|left, right| left.pkg.version == right.pkg.version);
|
summaries.dedup_by(|left, right| left.pkg.version == right.pkg.version);
|
||||||
@ -76,12 +81,13 @@ impl InMemorySource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn packages(&self) -> &BTreeMap<String, Vec<PackageSummary>> {
|
pub fn packages(&self) -> &BTreeMap<String, Vec<PackageSummary>> {
|
||||||
&self.packages
|
&self.named_packages
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, package_name: &str, version: &Version) -> Option<&PackageSummary> {
|
pub fn get(&self, id: &PackageId) -> Option<&PackageSummary> {
|
||||||
let summaries = self.packages.get(package_name)?;
|
let ident = id.as_named()?;
|
||||||
summaries.iter().find(|s| s.pkg.version == *version)
|
let summaries = self.named_packages.get(&ident.name)?;
|
||||||
|
summaries.iter().find(|s| s.pkg.version == ident.version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +97,7 @@ impl Source for InMemorySource {
|
|||||||
async fn query(&self, package: &PackageSpecifier) -> Result<Vec<PackageSummary>, QueryError> {
|
async fn query(&self, package: &PackageSpecifier) -> Result<Vec<PackageSummary>, QueryError> {
|
||||||
match package {
|
match package {
|
||||||
PackageSpecifier::Registry { full_name, version } => {
|
PackageSpecifier::Registry { full_name, version } => {
|
||||||
match self.packages.get(full_name) {
|
match self.named_packages.get(full_name) {
|
||||||
Some(summaries) => {
|
Some(summaries) => {
|
||||||
let matches: Vec<_> = summaries
|
let matches: Vec<_> = summaries
|
||||||
.iter()
|
.iter()
|
||||||
@ -117,6 +123,13 @@ impl Source for InMemorySource {
|
|||||||
None => Err(QueryError::NotFound),
|
None => Err(QueryError::NotFound),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PackageSpecifier::HashSha256(hash) => self
|
||||||
|
.hash_packages
|
||||||
|
.get(hash)
|
||||||
|
.map(|x| vec![x.clone()])
|
||||||
|
.ok_or_else(|| QueryError::NoMatches {
|
||||||
|
archived_versions: Vec::new(),
|
||||||
|
}),
|
||||||
PackageSpecifier::Url(_) | PackageSpecifier::Path(_) => Err(QueryError::Unsupported),
|
PackageSpecifier::Url(_) | PackageSpecifier::Path(_) => Err(QueryError::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,15 +166,15 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
source
|
source
|
||||||
.packages
|
.named_packages
|
||||||
.keys()
|
.keys()
|
||||||
.map(|k| k.as_str())
|
.map(|k| k.as_str())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
["python", "sharrattj/bash", "sharrattj/coreutils"]
|
["python", "sharrattj/bash", "sharrattj/coreutils"]
|
||||||
);
|
);
|
||||||
assert_eq!(source.packages["sharrattj/coreutils"].len(), 2);
|
assert_eq!(source.named_packages["sharrattj/coreutils"].len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
source.packages["sharrattj/bash"][0],
|
source.named_packages["sharrattj/bash"][0],
|
||||||
PackageSummary {
|
PackageSummary {
|
||||||
pkg: PackageInfo {
|
pkg: PackageInfo {
|
||||||
name: "sharrattj/bash".to_string(),
|
name: "sharrattj/bash".to_string(),
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{bail, Context, Error};
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -17,6 +17,8 @@ use webc::{
|
|||||||
|
|
||||||
use crate::runtime::resolver::PackageId;
|
use crate::runtime::resolver::PackageId;
|
||||||
|
|
||||||
|
use super::outputs::PackageIdent;
|
||||||
|
|
||||||
/// A reference to *some* package somewhere that the user wants to run.
|
/// A reference to *some* package somewhere that the user wants to run.
|
||||||
///
|
///
|
||||||
/// # Security Considerations
|
/// # Security Considerations
|
||||||
@ -32,6 +34,7 @@ pub enum PackageSpecifier {
|
|||||||
full_name: String,
|
full_name: String,
|
||||||
version: VersionReq,
|
version: VersionReq,
|
||||||
},
|
},
|
||||||
|
HashSha256(String),
|
||||||
Url(Url),
|
Url(Url),
|
||||||
/// A `*.webc` file on disk.
|
/// A `*.webc` file on disk.
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
@ -47,6 +50,14 @@ impl FromStr for PackageSpecifier {
|
|||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.starts_with("sha256:") {
|
||||||
|
let rest = &s[7..];
|
||||||
|
if rest.len() != 64 {
|
||||||
|
bail!("Invalid sha256:{rest} package hash: not a valid sha256 hash, expected 64 characters");
|
||||||
|
}
|
||||||
|
return Ok(Self::HashSha256(rest.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
// There is no function in std for checking if a string is a valid path
|
// There is no function in std for checking if a string is a valid path
|
||||||
// and we can't do Path::new(s).exists() because that assumes the
|
// and we can't do Path::new(s).exists() because that assumes the
|
||||||
// package being specified is on the local filesystem, so let's make a
|
// package being specified is on the local filesystem, so let's make a
|
||||||
@ -112,6 +123,7 @@ impl Display for PackageSpecifier {
|
|||||||
}
|
}
|
||||||
PackageSpecifier::Url(url) => Display::fmt(url, f),
|
PackageSpecifier::Url(url) => Display::fmt(url, f),
|
||||||
PackageSpecifier::Path(path) => write!(f, "{}", path.display()),
|
PackageSpecifier::Path(path) => write!(f, "{}", path.display()),
|
||||||
|
PackageSpecifier::HashSha256(hash) => write!(f, "sha256:{hash}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,10 +243,10 @@ impl PackageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> PackageId {
|
pub fn id(&self) -> PackageId {
|
||||||
PackageId {
|
PackageId::Named(PackageIdent {
|
||||||
package_name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
version: self.version.clone(),
|
version: self.version.clone(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub use self::{
|
|||||||
},
|
},
|
||||||
multi_source::{MultiSource, MultiSourceStrategy},
|
multi_source::{MultiSource, MultiSourceStrategy},
|
||||||
outputs::{
|
outputs::{
|
||||||
DependencyGraph, Edge, ItemLocation, Node, PackageId, Resolution,
|
DependencyGraph, Edge, ItemLocation, Node, PackageId, PackageIdent, Resolution,
|
||||||
ResolvedFileSystemMapping, ResolvedPackage,
|
ResolvedFileSystemMapping, ResolvedPackage,
|
||||||
},
|
},
|
||||||
resolve::{resolve, ResolveError},
|
resolve::{resolve, ResolveError},
|
||||||
|
@ -27,20 +27,55 @@ pub struct ItemLocation {
|
|||||||
pub package: PackageId,
|
pub package: PackageId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier for a package within a dependency graph.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct PackageId {
|
pub struct PackageIdent {
|
||||||
pub package_name: String,
|
pub name: String,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for PackageIdent {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}@{}", self.name, self.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum PackageId {
|
||||||
|
Named(PackageIdent),
|
||||||
|
HashSha256(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageId {
|
||||||
|
pub fn new_named(name: impl Into<String>, version: Version) -> Self {
|
||||||
|
Self::Named(PackageIdent {
|
||||||
|
name: name.into(),
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_named(&self) -> Option<&PackageIdent> {
|
||||||
|
if let Self::Named(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_hash_sha256(&self) -> Option<&String> {
|
||||||
|
if let Self::HashSha256(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for PackageId {
|
impl Display for PackageId {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
let PackageId {
|
match self {
|
||||||
package_name,
|
Self::Named(ident) => ident.fmt(f),
|
||||||
version,
|
Self::HashSha256(hash) => write!(f, "sha256:{}", hash),
|
||||||
} = self;
|
}
|
||||||
write!(f, "{package_name}@{version}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ fn registry_error_message(specifier: &PackageSpecifier) -> String {
|
|||||||
PackageSpecifier::Registry { full_name, version } => {
|
PackageSpecifier::Registry { full_name, version } => {
|
||||||
format!("Unable to find \"{full_name}@{version}\" in the registry")
|
format!("Unable to find \"{full_name}@{version}\" in the registry")
|
||||||
}
|
}
|
||||||
|
PackageSpecifier::HashSha256(hash) => {
|
||||||
|
format!("Unable to find package \"{hash}\" in the registry")
|
||||||
|
}
|
||||||
PackageSpecifier::Url(url) => format!("Unable to resolve \"{url}\""),
|
PackageSpecifier::Url(url) => format!("Unable to resolve \"{url}\""),
|
||||||
PackageSpecifier::Path(path) => {
|
PackageSpecifier::Path(path) => {
|
||||||
format!("Unable to load \"{}\" from disk", path.display())
|
format!("Unable to load \"{}\" from disk", path.display())
|
||||||
@ -78,14 +81,7 @@ impl ResolveError {
|
|||||||
fn print_cycle(packages: &[PackageId]) -> String {
|
fn print_cycle(packages: &[PackageId]) -> String {
|
||||||
packages
|
packages
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pkg_id| {
|
.map(|pkg_id| pkg_id.to_string())
|
||||||
let PackageId {
|
|
||||||
package_name,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
} = pkg_id;
|
|
||||||
format!("{package_name}@{version}")
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" → ")
|
.join(" → ")
|
||||||
}
|
}
|
||||||
@ -257,15 +253,14 @@ where
|
|||||||
{
|
{
|
||||||
let mut package_versions: BTreeMap<&str, HashSet<&Version>> = BTreeMap::new();
|
let mut package_versions: BTreeMap<&str, HashSet<&Version>> = BTreeMap::new();
|
||||||
|
|
||||||
for PackageId {
|
for id in package_ids {
|
||||||
package_name,
|
let Some(id) = id.as_named() else {
|
||||||
version,
|
continue;
|
||||||
} in package_ids
|
};
|
||||||
{
|
|
||||||
package_versions
|
package_versions
|
||||||
.entry(package_name)
|
.entry(&id.name)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(version);
|
.insert(&id.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (package_name, versions) in package_versions {
|
for (package_name, versions) in package_versions {
|
||||||
@ -304,8 +299,7 @@ fn resolve_package(dependency_graph: &DependencyGraph) -> Result<ResolvedPackage
|
|||||||
if let Some(entry) = &pkg.entrypoint {
|
if let Some(entry) = &pkg.entrypoint {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
entrypoint = entry.as_str(),
|
entrypoint = entry.as_str(),
|
||||||
parent.name=id.package_name.as_str(),
|
parent=%id,
|
||||||
parent.version=%id.version,
|
|
||||||
"Inheriting the entrypoint",
|
"Inheriting the entrypoint",
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -327,16 +321,14 @@ fn resolve_package(dependency_graph: &DependencyGraph) -> Result<ResolvedPackage
|
|||||||
entry.insert(resolved);
|
entry.insert(resolved);
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
command.name=cmd.name.as_str(),
|
command.name=cmd.name.as_str(),
|
||||||
pkg.name=id.package_name.as_str(),
|
pkg=%id,
|
||||||
pkg.version=%id.version,
|
|
||||||
"Discovered command",
|
"Discovered command",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
std::collections::btree_map::Entry::Occupied(_) => {
|
std::collections::btree_map::Entry::Occupied(_) => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
command.name=cmd.name.as_str(),
|
command.name=cmd.name.as_str(),
|
||||||
pkg.name=id.package_name.as_str(),
|
pkg=%id,
|
||||||
pkg.version=%id.version,
|
|
||||||
"Ignoring duplicate command",
|
"Ignoring duplicate command",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -438,11 +430,15 @@ mod tests {
|
|||||||
registry
|
registry
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, package: &str, version: &str) -> &PackageSummary {
|
fn get(&self, id: &PackageId) -> &PackageSummary {
|
||||||
let version = version.parse().unwrap();
|
self.0.get(id).unwrap()
|
||||||
self.0.get(package, &version).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn get_named(&self, name: &str, version: &str) -> &PackageSummary {
|
||||||
|
// let id = PackageId::new_named(name, version.parse().unwrap());
|
||||||
|
// self.get(&id)
|
||||||
|
// }
|
||||||
|
|
||||||
fn start_dependency_graph(&self) -> DependencyGraphBuilder<'_> {
|
fn start_dependency_graph(&self) -> DependencyGraphBuilder<'_> {
|
||||||
DependencyGraphBuilder {
|
DependencyGraphBuilder {
|
||||||
dependencies: BTreeMap::new(),
|
dependencies: BTreeMap::new(),
|
||||||
@ -542,16 +538,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'source> DependencyGraphBuilder<'source> {
|
impl<'source> DependencyGraphBuilder<'source> {
|
||||||
fn insert(
|
fn insert(&mut self, id: PackageId) -> DependencyGraphEntryBuilder<'source, '_> {
|
||||||
&mut self,
|
let _ = self.source.get(&id).unwrap();
|
||||||
package: &str,
|
|
||||||
version: &str,
|
|
||||||
) -> DependencyGraphEntryBuilder<'source, '_> {
|
|
||||||
let version = version.parse().unwrap();
|
|
||||||
let pkg_id = self.source.get(package, &version).unwrap().package_id();
|
|
||||||
DependencyGraphEntryBuilder {
|
DependencyGraphEntryBuilder {
|
||||||
builder: self,
|
builder: self,
|
||||||
pkg_id,
|
pkg_id: id,
|
||||||
dependencies: BTreeMap::new(),
|
dependencies: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,16 +553,14 @@ mod tests {
|
|||||||
|
|
||||||
/// Using the dependency mapping that we've been building up, construct
|
/// Using the dependency mapping that we've been building up, construct
|
||||||
/// a dependency graph using the specified root package.
|
/// a dependency graph using the specified root package.
|
||||||
fn graph(self, root_name: &str, version: &str) -> DependencyGraph {
|
fn graph(self, root_id: PackageId) -> DependencyGraph {
|
||||||
let version = version.parse().unwrap();
|
let _ = self.source.get(&root_id).unwrap();
|
||||||
let root_id = self.source.get(root_name, &version).unwrap().package_id();
|
|
||||||
|
|
||||||
let mut graph = DiGraph::new();
|
let mut graph = DiGraph::new();
|
||||||
let mut nodes = BTreeMap::new();
|
let mut nodes = BTreeMap::new();
|
||||||
|
|
||||||
for id in self.dependencies.keys() {
|
for id in self.dependencies.keys() {
|
||||||
let PackageSummary { pkg, dist } =
|
let PackageSummary { pkg, dist } = self.source.get(id).unwrap();
|
||||||
self.source.get(&id.package_name, &id.version).unwrap();
|
|
||||||
let index = graph.add_node(Node {
|
let index = graph.add_node(Node {
|
||||||
id: pkg.id(),
|
id: pkg.id(),
|
||||||
pkg: pkg.clone(),
|
pkg: pkg.clone(),
|
||||||
@ -608,18 +597,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'source, 'builder> DependencyGraphEntryBuilder<'source, 'builder> {
|
impl<'source, 'builder> DependencyGraphEntryBuilder<'source, 'builder> {
|
||||||
fn with_dependency(&mut self, name: &str, version: &str) -> &mut Self {
|
fn with_dependency(&mut self, id: &PackageId) -> &mut Self {
|
||||||
self.with_aliased_dependency(name, name, version)
|
let name = &id.as_named().unwrap().name;
|
||||||
|
self.with_aliased_dependency(name, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_aliased_dependency(&mut self, alias: &str, name: &str, version: &str) -> &mut Self {
|
fn with_aliased_dependency(&mut self, alias: &str, id: &PackageId) -> &mut Self {
|
||||||
let version = version.parse().unwrap();
|
let dep_id = self.builder.source.get(id).unwrap().package_id();
|
||||||
let dep_id = self
|
|
||||||
.builder
|
|
||||||
.source
|
|
||||||
.get(name, &version)
|
|
||||||
.unwrap()
|
|
||||||
.package_id();
|
|
||||||
self.dependencies.insert(alias.to_string(), dep_id);
|
self.dependencies.insert(alias.to_string(), dep_id);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -667,14 +651,15 @@ mod tests {
|
|||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder.register("root", "1.0.0");
|
builder.register("root", "1.0.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let id = PackageId::new_named("root", Version::parse("1.0.0").unwrap());
|
||||||
|
let root = builder.get(&id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph.insert("root", "1.0.0");
|
dependency_graph.insert(id);
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -692,14 +677,15 @@ mod tests {
|
|||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder.register("root", "1.0.0").with_command("asdf");
|
builder.register("root", "1.0.0").with_command("asdf");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let root = builder.get(&id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph.insert("root", "1.0.0");
|
dependency_graph.insert(id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -725,17 +711,17 @@ mod tests {
|
|||||||
.with_dependency("dep", "=1.0.0");
|
.with_dependency("dep", "=1.0.0");
|
||||||
builder.register("dep", "1.0.0");
|
builder.register("dep", "1.0.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let root = builder.get(&id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap());
|
||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph.insert(id.clone()).with_dependency(&dep_id);
|
||||||
.insert("root", "1.0.0")
|
dependency_graph.insert(dep_id.clone());
|
||||||
.with_dependency("dep", "1.0.0");
|
|
||||||
dependency_graph.insert("dep", "1.0.0");
|
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -750,6 +736,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn linear_dependency_chain() {
|
async fn linear_dependency_chain() {
|
||||||
|
let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap());
|
||||||
|
let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap());
|
||||||
|
let third_id = PackageId::new_named("third", "1.0.0".parse().unwrap());
|
||||||
|
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("first", "1.0.0")
|
.register("first", "1.0.0")
|
||||||
@ -759,7 +749,7 @@ mod tests {
|
|||||||
.with_dependency("third", "=1.0.0");
|
.with_dependency("third", "=1.0.0");
|
||||||
builder.register("third", "1.0.0");
|
builder.register("third", "1.0.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("first", "1.0.0");
|
let root = builder.get(&first_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -767,12 +757,12 @@ mod tests {
|
|||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("first", "1.0.0")
|
.insert(first_id.clone())
|
||||||
.with_dependency("second", "1.0.0");
|
.with_dependency(&second_id);
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("second", "1.0.0")
|
.insert(second_id.clone())
|
||||||
.with_dependency("third", "1.0.0");
|
.with_dependency(&third_id);
|
||||||
dependency_graph.insert("third", "1.0.0");
|
dependency_graph.insert(third_id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -787,6 +777,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn pick_the_latest_dependency_when_multiple_are_possible() {
|
async fn pick_the_latest_dependency_when_multiple_are_possible() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -795,17 +786,18 @@ mod tests {
|
|||||||
builder.register("dep", "1.0.1");
|
builder.register("dep", "1.0.1");
|
||||||
builder.register("dep", "1.0.2");
|
builder.register("dep", "1.0.2");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.2".parse().unwrap());
|
||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("root", "1.0.0")
|
.insert(root_id.clone())
|
||||||
.with_dependency("dep", "1.0.2");
|
.with_dependency(&dep_id);
|
||||||
dependency_graph.insert("dep", "1.0.2");
|
dependency_graph.insert(dep_id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -820,6 +812,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn version_merging_isnt_implemented_yet() {
|
async fn version_merging_isnt_implemented_yet() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -836,7 +829,7 @@ mod tests {
|
|||||||
builder.register("common", "1.2.0");
|
builder.register("common", "1.2.0");
|
||||||
builder.register("common", "1.5.0");
|
builder.register("common", "1.5.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let result = resolve(&root.package_id(), &root.pkg, ®istry).await;
|
let result = resolve(&root.package_id(), &root.pkg, ®istry).await;
|
||||||
|
|
||||||
@ -861,6 +854,11 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore = "Version merging isn't implemented"]
|
#[ignore = "Version merging isn't implemented"]
|
||||||
async fn merge_compatible_versions() {
|
async fn merge_compatible_versions() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap());
|
||||||
|
let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap());
|
||||||
|
let common_id = PackageId::new_named("common", "1.2.0".parse().unwrap());
|
||||||
|
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -877,7 +875,7 @@ mod tests {
|
|||||||
builder.register("common", "1.2.0");
|
builder.register("common", "1.2.0");
|
||||||
builder.register("common", "1.5.0");
|
builder.register("common", "1.5.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -885,16 +883,16 @@ mod tests {
|
|||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("root", "1.0.0")
|
.insert(root_id.clone())
|
||||||
.with_dependency("first", "1.0.0")
|
.with_dependency(&first_id)
|
||||||
.with_dependency("second", "1.0.0");
|
.with_dependency(&second_id);
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("first", "1.0.0")
|
.insert(first_id.clone())
|
||||||
.with_dependency("common", "1.2.0");
|
.with_dependency(&common_id);
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("second", "1.0.0")
|
.insert(second_id.clone())
|
||||||
.with_dependency("common", "1.2.0");
|
.with_dependency(&common_id);
|
||||||
dependency_graph.insert("common", "1.2.0");
|
dependency_graph.insert(common_id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -909,6 +907,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn commands_from_dependencies_end_up_in_the_package() {
|
async fn commands_from_dependencies_end_up_in_the_package() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap());
|
||||||
|
let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -921,7 +922,7 @@ mod tests {
|
|||||||
.register("second", "1.0.0")
|
.register("second", "1.0.0")
|
||||||
.with_command("second-command");
|
.with_command("second-command");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -929,11 +930,11 @@ mod tests {
|
|||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("root", "1.0.0")
|
.insert(root_id.clone())
|
||||||
.with_dependency("first", "1.0.0")
|
.with_dependency(&first_id)
|
||||||
.with_dependency("second", "1.0.0");
|
.with_dependency(&second_id);
|
||||||
dependency_graph.insert("first", "1.0.0");
|
dependency_graph.insert(first_id.clone());
|
||||||
dependency_graph.insert("second", "1.0.0");
|
dependency_graph.insert(second_id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -942,11 +943,11 @@ mod tests {
|
|||||||
commands: map! {
|
commands: map! {
|
||||||
"first-command" => ItemLocation {
|
"first-command" => ItemLocation {
|
||||||
name: "first-command".to_string(),
|
name: "first-command".to_string(),
|
||||||
package: builder.get("first", "1.0.0").package_id(),
|
package: builder.get(&first_id).package_id(),
|
||||||
},
|
},
|
||||||
"second-command" => ItemLocation {
|
"second-command" => ItemLocation {
|
||||||
name: "second-command".to_string(),
|
name: "second-command".to_string(),
|
||||||
package: builder.get("second", "1.0.0").package_id(),
|
package: builder.get(&second_id).package_id(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
entrypoint: None,
|
entrypoint: None,
|
||||||
@ -958,6 +959,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore = "TODO: Re-order the way commands are resolved"]
|
#[ignore = "TODO: Re-order the way commands are resolved"]
|
||||||
async fn commands_in_root_shadow_their_dependencies() {
|
async fn commands_in_root_shadow_their_dependencies() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -965,7 +968,7 @@ mod tests {
|
|||||||
.with_command("command");
|
.with_command("command");
|
||||||
builder.register("dep", "1.0.0").with_command("command");
|
builder.register("dep", "1.0.0").with_command("command");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -973,9 +976,9 @@ mod tests {
|
|||||||
|
|
||||||
let mut dependency_graph = builder.start_dependency_graph();
|
let mut dependency_graph = builder.start_dependency_graph();
|
||||||
dependency_graph
|
dependency_graph
|
||||||
.insert("root", "1.0.0")
|
.insert(root_id.clone())
|
||||||
.with_dependency("dep", "1.0.0");
|
.with_dependency(&dep_id);
|
||||||
dependency_graph.insert("dep", "1.0.0");
|
dependency_graph.insert(dep_id.clone());
|
||||||
assert_eq!(deps(&resolution), dependency_graph.finish());
|
assert_eq!(deps(&resolution), dependency_graph.finish());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolution.package,
|
resolution.package,
|
||||||
@ -984,7 +987,7 @@ mod tests {
|
|||||||
commands: map! {
|
commands: map! {
|
||||||
"command" => ItemLocation {
|
"command" => ItemLocation {
|
||||||
name: "command".to_string(),
|
name: "command".to_string(),
|
||||||
package: builder.get("root", "1.0.0").package_id(),
|
package: builder.get(&root_id).package_id(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
entrypoint: None,
|
entrypoint: None,
|
||||||
@ -995,6 +998,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn cyclic_dependencies() {
|
async fn cyclic_dependencies() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap());
|
||||||
|
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -1003,7 +1009,7 @@ mod tests {
|
|||||||
.register("dep", "1.0.0")
|
.register("dep", "1.0.0")
|
||||||
.with_dependency("root", "=1.0.0");
|
.with_dependency("root", "=1.0.0");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let err = resolve(&root.package_id(), &root.pkg, ®istry)
|
let err = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -1013,15 +1019,18 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
cycle,
|
cycle,
|
||||||
[
|
[
|
||||||
builder.get("root", "1.0.0").package_id(),
|
builder.get(&root_id).package_id(),
|
||||||
builder.get("dep", "1.0.0").package_id(),
|
builder.get(&dep_id).package_id(),
|
||||||
builder.get("root", "1.0.0").package_id(),
|
builder.get(&root_id).package_id(),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn entrypoint_is_inherited() {
|
async fn entrypoint_is_inherited() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap());
|
||||||
|
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -1031,7 +1040,7 @@ mod tests {
|
|||||||
.with_command("entry")
|
.with_command("entry")
|
||||||
.with_entrypoint("entry");
|
.with_entrypoint("entry");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -1044,7 +1053,7 @@ mod tests {
|
|||||||
commands: map! {
|
commands: map! {
|
||||||
"entry" => ItemLocation {
|
"entry" => ItemLocation {
|
||||||
name: "entry".to_string(),
|
name: "entry".to_string(),
|
||||||
package: builder.get("dep", "1.0.0").package_id(),
|
package: builder.get(&dep_id).package_id(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
entrypoint: Some("entry".to_string()),
|
entrypoint: Some("entry".to_string()),
|
||||||
@ -1055,6 +1064,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn infer_entrypoint_if_unspecified_and_only_one_command_in_root_package() {
|
async fn infer_entrypoint_if_unspecified_and_only_one_command_in_root_package() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -1062,7 +1072,7 @@ mod tests {
|
|||||||
.with_dependency("dep", "=1.0.0");
|
.with_dependency("dep", "=1.0.0");
|
||||||
builder.register("dep", "1.0.0").with_command("entry");
|
builder.register("dep", "1.0.0").with_command("entry");
|
||||||
let registry = builder.finish();
|
let registry = builder.finish();
|
||||||
let root = builder.get("root", "1.0.0");
|
let root = builder.get(&root_id);
|
||||||
|
|
||||||
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
let resolution = resolve(&root.package_id(), &root.pkg, ®istry)
|
||||||
.await
|
.await
|
||||||
@ -1074,18 +1084,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cyclic_error_message() {
|
fn cyclic_error_message() {
|
||||||
let cycle = [
|
let cycle = [
|
||||||
PackageId {
|
PackageId::new_named("root", "1.0.0".parse().unwrap()),
|
||||||
package_name: "root".to_string(),
|
PackageId::new_named("dep", "1.0.0".parse().unwrap()),
|
||||||
version: "1.0.0".parse().unwrap(),
|
PackageId::new_named("root", "1.0.0".parse().unwrap()),
|
||||||
},
|
|
||||||
PackageId {
|
|
||||||
package_name: "dep".to_string(),
|
|
||||||
version: "1.0.0".parse().unwrap(),
|
|
||||||
},
|
|
||||||
PackageId {
|
|
||||||
package_name: "root".to_string(),
|
|
||||||
version: "1.0.0".parse().unwrap(),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let message = print_cycle(&cycle);
|
let message = print_cycle(&cycle);
|
||||||
@ -1095,11 +1096,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filesystem_with_one_package_and_no_fs_tables() {
|
fn filesystem_with_one_package_and_no_fs_tables() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder.register("root", "1.0.0");
|
builder.register("root", "1.0.0");
|
||||||
let mut dep_builder = builder.start_dependency_graph();
|
let mut dep_builder = builder.start_dependency_graph();
|
||||||
dep_builder.insert("root", "1.0.0");
|
dep_builder.insert(root_id.clone());
|
||||||
let graph = dep_builder.graph("root", "1.0.0");
|
let graph = dep_builder.graph(root_id.clone());
|
||||||
|
|
||||||
let pkg = resolve_package(&graph).unwrap();
|
let pkg = resolve_package(&graph).unwrap();
|
||||||
|
|
||||||
@ -1108,13 +1110,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filesystem_with_one_package_and_one_fs_tables() {
|
fn filesystem_with_one_package_and_one_fs_tables() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
.with_fs_mapping("atom", "/publisher/lib", "/lib");
|
.with_fs_mapping("atom", "/publisher/lib", "/lib");
|
||||||
let mut dep_builder = builder.start_dependency_graph();
|
let mut dep_builder = builder.start_dependency_graph();
|
||||||
dep_builder.insert("root", "1.0.0");
|
dep_builder.insert(root_id.clone());
|
||||||
let graph = dep_builder.graph("root", "1.0.0");
|
let graph = dep_builder.graph(root_id.clone());
|
||||||
|
|
||||||
let pkg = resolve_package(&graph).unwrap();
|
let pkg = resolve_package(&graph).unwrap();
|
||||||
|
|
||||||
@ -1124,13 +1127,17 @@ mod tests {
|
|||||||
mount_path: PathBuf::from("/lib"),
|
mount_path: PathBuf::from("/lib"),
|
||||||
original_path: "/publisher/lib".to_string(),
|
original_path: "/publisher/lib".to_string(),
|
||||||
volume_name: "atom".to_string(),
|
volume_name: "atom".to_string(),
|
||||||
package: builder.get("root", "1.0.0").package_id(),
|
package: builder.get(&root_id).package_id(),
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_fs_mappings_from_multiple_packages() {
|
fn merge_fs_mappings_from_multiple_packages() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap());
|
||||||
|
let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap());
|
||||||
|
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -1149,12 +1156,12 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let mut dep_builder = builder.start_dependency_graph();
|
let mut dep_builder = builder.start_dependency_graph();
|
||||||
dep_builder
|
dep_builder
|
||||||
.insert("root", "1.0.0")
|
.insert(root_id.clone())
|
||||||
.with_dependency("first", "1.0.0")
|
.with_dependency(&first_id)
|
||||||
.with_dependency("second", "1.0.0");
|
.with_dependency(&second_id);
|
||||||
dep_builder.insert("first", "1.0.0");
|
dep_builder.insert(first_id.clone());
|
||||||
dep_builder.insert("second", "1.0.0");
|
dep_builder.insert(second_id.clone());
|
||||||
let graph = dep_builder.graph("root", "1.0.0");
|
let graph = dep_builder.graph(root_id.clone());
|
||||||
|
|
||||||
let pkg = resolve_package(&graph).unwrap();
|
let pkg = resolve_package(&graph).unwrap();
|
||||||
|
|
||||||
@ -1165,19 +1172,19 @@ mod tests {
|
|||||||
mount_path: PathBuf::from("/root"),
|
mount_path: PathBuf::from("/root"),
|
||||||
original_path: "/root".to_string(),
|
original_path: "/root".to_string(),
|
||||||
volume_name: "atom".to_string(),
|
volume_name: "atom".to_string(),
|
||||||
package: builder.get("root", "1.0.0").package_id(),
|
package: builder.get(&root_id).package_id(),
|
||||||
},
|
},
|
||||||
ResolvedFileSystemMapping {
|
ResolvedFileSystemMapping {
|
||||||
mount_path: PathBuf::from("/usr/local/lib/second"),
|
mount_path: PathBuf::from("/usr/local/lib/second"),
|
||||||
original_path: "/usr/local/lib/second".to_string(),
|
original_path: "/usr/local/lib/second".to_string(),
|
||||||
volume_name: "atom".to_string(),
|
volume_name: "atom".to_string(),
|
||||||
package: builder.get("second", "1.0.0").package_id(),
|
package: builder.get(&second_id).package_id(),
|
||||||
},
|
},
|
||||||
ResolvedFileSystemMapping {
|
ResolvedFileSystemMapping {
|
||||||
mount_path: PathBuf::from("/usr/local/lib/first"),
|
mount_path: PathBuf::from("/usr/local/lib/first"),
|
||||||
volume_name: "atom".to_string(),
|
volume_name: "atom".to_string(),
|
||||||
original_path: "/usr/local/lib/first".to_string(),
|
original_path: "/usr/local/lib/first".to_string(),
|
||||||
package: builder.get("first", "1.0.0").package_id(),
|
package: builder.get(&first_id).package_id(),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -1185,6 +1192,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn use_fs_mapping_from_dependency() {
|
fn use_fs_mapping_from_dependency() {
|
||||||
|
let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap());
|
||||||
|
let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap());
|
||||||
let mut builder = RegistryBuilder::new();
|
let mut builder = RegistryBuilder::new();
|
||||||
builder
|
builder
|
||||||
.register("root", "1.0.0")
|
.register("root", "1.0.0")
|
||||||
@ -1192,11 +1201,9 @@ mod tests {
|
|||||||
.with_fs_mapping_from_dependency("dep-volume", "/root", "/root", "dep");
|
.with_fs_mapping_from_dependency("dep-volume", "/root", "/root", "dep");
|
||||||
builder.register("dep", "1.0.0");
|
builder.register("dep", "1.0.0");
|
||||||
let mut dep_builder = builder.start_dependency_graph();
|
let mut dep_builder = builder.start_dependency_graph();
|
||||||
dep_builder
|
dep_builder.insert(root_id.clone()).with_dependency(&dep_id);
|
||||||
.insert("root", "1.0.0")
|
dep_builder.insert(dep_id.clone());
|
||||||
.with_dependency("dep", "1.0.0");
|
let graph = dep_builder.graph(root_id.clone());
|
||||||
dep_builder.insert("dep", "1.0.0");
|
|
||||||
let graph = dep_builder.graph("root", "1.0.0");
|
|
||||||
|
|
||||||
let pkg = resolve_package(&graph).unwrap();
|
let pkg = resolve_package(&graph).unwrap();
|
||||||
|
|
||||||
@ -1206,7 +1213,7 @@ mod tests {
|
|||||||
mount_path: PathBuf::from("/root"),
|
mount_path: PathBuf::from("/root"),
|
||||||
original_path: "/root".to_string(),
|
original_path: "/root".to_string(),
|
||||||
volume_name: "dep-volume".to_string(),
|
volume_name: "dep-volume".to_string(),
|
||||||
package: builder.get("dep", "1.0.0").package_id(),
|
package: builder.get(&dep_id).package_id(),
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1043,7 +1043,7 @@ impl WasiEnv {
|
|||||||
/// [cmd-atom]: crate::bin_factory::BinaryPackageCommand::atom()
|
/// [cmd-atom]: crate::bin_factory::BinaryPackageCommand::atom()
|
||||||
/// [pkg-fs]: crate::bin_factory::BinaryPackage::webc_fs
|
/// [pkg-fs]: crate::bin_factory::BinaryPackage::webc_fs
|
||||||
pub fn use_package(&self, pkg: &BinaryPackage) -> Result<(), WasiStateCreationError> {
|
pub fn use_package(&self, pkg: &BinaryPackage) -> Result<(), WasiStateCreationError> {
|
||||||
tracing::trace!(packagae=%pkg.package_name, "merging package dependency into wasi environment");
|
tracing::trace!(package=%pkg.id, "merging package dependency into wasi environment");
|
||||||
let root_fs = &self.state.fs.root_fs;
|
let root_fs = &self.state.fs.root_fs;
|
||||||
|
|
||||||
// We first need to copy any files in the package over to the
|
// We first need to copy any files in the package over to the
|
||||||
@ -1089,7 +1089,7 @@ impl WasiEnv {
|
|||||||
{
|
{
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"failed to add package [{}] command [{}] - {}",
|
"failed to add package [{}] command [{}] - {}",
|
||||||
pkg.package_name,
|
pkg.id,
|
||||||
command.name(),
|
command.name(),
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
@ -1115,7 +1115,7 @@ impl WasiEnv {
|
|||||||
.set_binary(path.as_os_str().to_string_lossy().as_ref(), package);
|
.set_binary(path.as_os_str().to_string_lossy().as_ref(), package);
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
package=%pkg.package_name,
|
package=%pkg.id,
|
||||||
command_name=command.name(),
|
command_name=command.name(),
|
||||||
path=%path.display(),
|
path=%path.display(),
|
||||||
"Injected a command into the filesystem",
|
"Injected a command into the filesystem",
|
||||||
|
Reference in New Issue
Block a user