Skip to content

Commit

Permalink
feat(*)!: Adds back support for the offline flag (#328)
Browse files Browse the repository at this point in the history
This adds back in support for offline deps. It required a change to the
dependency resolver to error early if offline was requested without a
lock file present

Signed-off-by: Taylor Thomas <taylor@cosmonic.com>
  • Loading branch information
thomastaylor312 committed Aug 9, 2024
1 parent 936a47f commit bcb2087
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 65 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ warg-protocol = "0.7.0"
warg-server = "0.7.0"
wasi-preview1-component-adapter-provider = "23.0.1"
wasm-metadata = "0.208.1"
wasm-pkg-client = { git = "https://github.com/bytecodealliance/wasm-pkg-tools.git", rev = "47ad11a549c23ac48ecee9226d395fc7c6063250" }
wasm-pkg-client = { git = "https://github.com/bytecodealliance/wasm-pkg-tools.git", rev = "c48006aa1bcff1e69f4f8fc6689249b314985ab1" }
wasmparser = "0.208.1"
wasmprinter = "0.208.1"
wat = "1.208.1"
Expand Down
113 changes: 67 additions & 46 deletions crates/core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,29 +451,42 @@ pub struct DependencyResolver<'a> {
}

impl<'a> DependencyResolver<'a> {
/// Creates a new dependency resolver.
pub fn new(config: Config, lock_file: Option<LockFileResolver<'a>>, cache: FileCache) -> Self {
let client = CachingClient::new(Client::new(config), cache);
DependencyResolver {
/// Creates a new dependency resolver. If `config` is `None`, then the resolver will be set to
/// offline mode and a lock file must be given as well. Anything that will require network
/// access will fail in offline mode.
pub fn new(
config: Option<Config>,
lock_file: Option<LockFileResolver<'a>>,
cache: FileCache,
) -> anyhow::Result<Self> {
if config.is_none() && lock_file.is_none() {
anyhow::bail!("lock file must be provided when offline mode is enabled");
}
let client = CachingClient::new(config.map(Client::new), cache);
Ok(DependencyResolver {
client: Arc::new(client),
lock_file,
registries: Default::default(),
resolutions: Default::default(),
}
})
}

/// Creates a new dependency resolver with the given client. This is useful when you already
/// have a client available
/// have a client available. If the client is set to offline mode, then a lock file must be
/// given or this will error
pub fn new_with_client(
client: Arc<CachingClient<FileCache>>,
lock_file: Option<LockFileResolver<'a>>,
) -> Self {
DependencyResolver {
) -> anyhow::Result<Self> {
if client.is_readonly() && lock_file.is_none() {
anyhow::bail!("lock file must be provided when offline mode is enabled");
}
Ok(DependencyResolver {
client,
lock_file,
registries: Default::default(),
resolutions: Default::default(),
}
})
}

/// Add a dependency to the resolver.
Expand Down Expand Up @@ -590,45 +603,53 @@ impl<'a> Registry<'a> {
// We need to clone a handle to the client because we mutably borrow self below. Might
// be worth replacing the mutable borrow with a RwLock down the line.
let client = self.client.clone();
let versions = load_package(
&mut self.packages,
&self.client.client,
dependency.package.clone(),
)
.await?
.with_context(|| {
format!(
"package `{name}` was not found in component registry `{registry}`",
name = dependency.package
)
})?;

let (selected_version, digest) = match &dependency.locked {
Some((version, digest)) => {
// The dependency had a lock file entry, so attempt to do an exact match first
let exact_req = VersionReq {
comparators: vec![Comparator {
op: Op::Exact,
major: version.major,
minor: Some(version.minor),
patch: Some(version.patch),
pre: version.pre.clone(),
}],
};

// If an exact match can't be found, fallback to the latest release to satisfy
// the version requirement; this can happen when packages are yanked. If we did
// find an exact match, return the digest for comparison after fetching the
// release
find_latest_release(versions, &exact_req).map(|v| (v, Some(digest))).or_else(|| find_latest_release(versions, dependency.version).map(|v| (v, None)))
}
None => find_latest_release(versions, dependency.version).map(|v| (v, None)),
}.with_context(|| format!("component registry package `{name}` has no release matching version requirement `{version}`", name = dependency.package, version = dependency.version))?;

let (selected_version, digest) = if client.is_readonly() {
dependency
.locked
.as_ref()
.map(|(ver, digest)| (ver, Some(digest)))
.ok_or_else(|| {
anyhow::anyhow!("Couldn't find locked dependency while in offline mode")
})?
} else {
let versions =
load_package(&mut self.packages, &self.client, dependency.package.clone())
.await?
.with_context(|| {
format!(
"package `{name}` was not found in component registry `{registry}`",
name = dependency.package
)
})?;

match &dependency.locked {
Some((version, digest)) => {
// The dependency had a lock file entry, so attempt to do an exact match first
let exact_req = VersionReq {
comparators: vec![Comparator {
op: Op::Exact,
major: version.major,
minor: Some(version.minor),
patch: Some(version.patch),
pre: version.pre.clone(),
}],
};

// If an exact match can't be found, fallback to the latest release to satisfy
// the version requirement; this can happen when packages are yanked. If we did
// find an exact match, return the digest for comparison after fetching the
// release
find_latest_release(versions, &exact_req).map(|v| (&v.version, Some(digest))).or_else(|| find_latest_release(versions, dependency.version).map(|v| (&v.version, None)))
}
None => find_latest_release(versions, dependency.version).map(|v| (&v.version, None)),
}.with_context(|| format!("component registry package `{name}` has no release matching version requirement `{version}`", name = dependency.package, version = dependency.version))?
};

// We need to clone a handle to the client because we mutably borrow self above. Might
// be worth replacing the mutable borrow with a RwLock down the line.
let release = client
.client
.get_release(&dependency.package, &selected_version.version)
.get_release(&dependency.package, selected_version)
.await?;
if let Some(digest) = digest {
if &release.content_digest != digest {
Expand Down Expand Up @@ -662,7 +683,7 @@ impl<'a> Registry<'a> {

async fn load_package<'b>(
packages: &'b mut HashMap<PackageRef, Vec<VersionInfo>>,
client: &Client,
client: &CachingClient<FileCache>,
package: PackageRef,
) -> Result<Option<&'b Vec<VersionInfo>>> {
match packages.entry(package) {
Expand Down
7 changes: 4 additions & 3 deletions crates/wit/src/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ use wasm_pkg_client::{caching::FileCache, PackageRef};
use crate::config::{Config, CONFIG_FILE_NAME};

async fn resolve_version(
pkg_config: wasm_pkg_client::Config,
pkg_config: Option<wasm_pkg_client::Config>,
package: &VersionedPackageName,
registry: &Option<String>,
file_cache: FileCache,
) -> Result<String> {
let mut resolver = DependencyResolver::new(pkg_config, None, file_cache);
let mut resolver = DependencyResolver::new(pkg_config, None, file_cache)?;
let dependency = Dependency::Package(RegistryPackage {
name: Some(package.name.clone()),
version: package
Expand Down Expand Up @@ -113,7 +113,8 @@ impl AddCommand {
}
None => {
let version =
resolve_version(pkg_config, &self.package, &self.registry, file_cache).await?;
resolve_version(Some(pkg_config), &self.package, &self.registry, file_cache)
.await?;

let package = RegistryPackage {
name: self.name.is_some().then(|| self.package.name.clone()),
Expand Down
6 changes: 3 additions & 3 deletions crates/wit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ async fn resolve_dependencies(
.transpose()?;

let mut resolver = DependencyResolver::new(
pkg_config,
Some(pkg_config),
lock_file.as_ref().map(LockFileResolver::new),
file_cache,
);
)?;

for (name, dep) in &config.dependencies {
resolver.add_dependency(name, dep).await?;
Expand Down Expand Up @@ -389,7 +389,7 @@ pub async fn update_lockfile(
file_cache: FileCache,
) -> Result<()> {
// Resolve all dependencies as if the lock file does not exist
let mut resolver = DependencyResolver::new(pkg_config, None, file_cache);
let mut resolver = DependencyResolver::new(Some(pkg_config), None, file_cache)?;
for (name, dep) in &config.dependencies {
resolver.add_dependency(name, dep).await?;
}
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo-component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ async fn main() -> Result<()> {
}

let spawn_args: Vec<_> = std::env::args().skip(1).collect();
let client = config.client(cache_dir).await?;
let client = config.client(cache_dir, cargo_args.offline).await?;
if let Err(e) = run_cargo_command(
client,
&config,
Expand Down
4 changes: 2 additions & 2 deletions src/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl AddCommand {
let config = Config::new(self.common.new_terminal(), self.common.config.clone())?;
let metadata = load_metadata(self.manifest_path.as_deref())?;

let client = config.client(self.common.cache_dir.clone()).await?;
let client = config.client(self.common.cache_dir.clone(), false).await?;

let spec = match &self.spec {
Some(spec) => Some(spec.clone()),
Expand Down Expand Up @@ -130,7 +130,7 @@ impl AddCommand {
client: Arc<CachingClient<FileCache>>,
name: &PackageRef,
) -> Result<String> {
let mut resolver = DependencyResolver::new_with_client(client, None);
let mut resolver = DependencyResolver::new_with_client(client, None)?;
let dependency = Dependency::Package(RegistryPackage {
name: Some(self.package.name.clone()),
version: self
Expand Down
4 changes: 2 additions & 2 deletions src/commands/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl NewCommand {
Some(s) => Some(format!("{s}@{version}", version = VersionReq::STAR).parse()?),
None => None,
};
let client = config.client(self.common.cache_dir.clone()).await?;
let client = config.client(self.common.cache_dir.clone(), false).await?;
let target = self.resolve_target(client, target).await?;
let source = self.generate_source(&target).await?;

Expand Down Expand Up @@ -531,7 +531,7 @@ world example {{
package,
world,
}) => {
let mut resolver = DependencyResolver::new_with_client(client, None);
let mut resolver = DependencyResolver::new_with_client(client, None)?;
let dependency = Dependency::Package(package);

resolver.add_dependency(&name, &dependency).await?;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl PublishCommand {
log::debug!("executing publish command");

let config = Config::new(self.common.new_terminal(), self.common.config.clone())?;
let client = config.client(self.common.cache_dir.clone()).await?;
let client = config.client(self.common.cache_dir.clone(), false).await?;

if let Some(target) = &self.target {
if !is_wasm_target(target) {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl UpdateCommand {
let packages = load_component_metadata(&metadata, [].iter(), true)?;

let lock_update_allowed = !self.frozen && !self.locked;
let client = config.client(self.common.cache_dir).await?;
let client = config.client(self.common.cache_dir, false).await?;
crate::update_lockfile(
client,
&config,
Expand Down
3 changes: 2 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,10 @@ impl Config {
pub async fn client(
&self,
cache: Option<PathBuf>,
offline: bool,
) -> anyhow::Result<Arc<CachingClient<FileCache>>> {
Ok(Arc::new(CachingClient::new(
Client::new(self.pkg_config.clone()),
(!offline).then(|| Client::new(self.pkg_config.clone())),
FileCache::new(cache_dir(cache)?).await?,
)))
}
Expand Down
4 changes: 2 additions & 2 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<'a> PackageDependencyResolution<'a> {
return Ok(Default::default());
}

let mut resolver = DependencyResolver::new_with_client(client, lock_file);
let mut resolver = DependencyResolver::new_with_client(client, lock_file)?;

for (name, dependency) in target_deps.iter() {
resolver.add_dependency(name, dependency).await?;
Expand All @@ -78,7 +78,7 @@ impl<'a> PackageDependencyResolution<'a> {
return Ok(Default::default());
}

let mut resolver = DependencyResolver::new_with_client(client, lock_file);
let mut resolver = DependencyResolver::new_with_client(client, lock_file)?;

for (name, dependency) in &metadata.section.dependencies {
resolver.add_dependency(name, dependency).await?;
Expand Down

0 comments on commit bcb2087

Please sign in to comment.