Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

wapm.io integration #43

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

97 changes: 97 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use humansize::{format_size, BINARY};
use owo_colors::OwoColorize;
use prettytable::{row, Table};
use tokio::runtime::Runtime;
use utils::registry::{download_module, gen_manifest, PackageRegistry, PackageSpec};
use utils::structs::Manifest;
use uuid::Uuid;

Expand Down Expand Up @@ -70,6 +71,12 @@ pub enum Command {
History,
/// Liveness check: ping at least one node on the mesh.
Ping,
/// Highly experimental: Pull a package from WAPM.io and store it in Serval Mesh
Pull {
/// The name of the software package, formatted as
/// [[protocol://]registry.tld/]author/packagename[@version][:module]
identifer: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can eventually make the identifier a type more specific than a string and have clap parse it for us. Some examples here. There is no need to do that in this PR, however.

},
}

static SERVAL_NODE_URL: Mutex<Option<String>> = Mutex::new(None);
Expand Down Expand Up @@ -289,6 +296,95 @@ fn blocking_maybe_discover_service_url(
Ok(format!("http://{addr}:{port}"))
}

/// Pull a Wasm package from a package manager, generate its manifest, and store it.
fn pull(identifer: String) -> Result<()> {
let pkg_spec = PackageSpec::try_from(identifer).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will teach you a new trick with Rust! Or maybe you already know this one, but here is let-else:

let pkg_spec = Ok(PackageSpec::try_from(identifer)) else {
   // tell the user what they got wrong
   // then exit with a non-zero code
}

This is nicer than panicking here if the format is wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhhh... nice. I was theoretically aware of let else but did not grok the specific usage. Will hunt down a bunch of unwrap()s and clean them up.

log::debug!("{:#?}", pkg_spec);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is absolutely zero reason to change this, but I note that the dbg!() macro is another option for this kind of fast printf debugging.

println!(
"📦 Identified package {}",
pkg_spec.profile_url().bold().blue()
);
println!("🏷 Using module {}", pkg_spec.module.bold().blue());
if pkg_spec.is_binary_cached() {
println!(
"✅ Binary for {} ({}) available locally.",
pkg_spec.fq_name().bold().green(),
pkg_spec.fq_digest()
);
// Creating a temporary manifest file
let manifest_path = gen_manifest(&pkg_spec).unwrap();
// Handing over to existing storage logic
upload_manifest(manifest_path)?;
} else {
println!(
"⌛️ Binary for {} not available locally, downloading...",
pkg_spec.fq_name().blue()
);
let mod_dl = download_module(&pkg_spec);
match mod_dl {
// This means the download function did not break. It does not mean that
// the executable was downloaded successfully... check HTTP status code.
Ok(status_code) => {
if status_code.is_success() {
println!(
"✅ Downloaded {} ({}) successfully.",
pkg_spec.fq_name().bold().green(),
pkg_spec.fq_digest()
);
// Creating a temporary manifest file
let manifest_path = gen_manifest(&pkg_spec).unwrap();
// Handing over to existing storage logic
upload_manifest(manifest_path)?;
} else if status_code.is_server_error() {
println!("🛑 Server error: {}", status_code);
println!(" There may be an issue with this package manager.");
} else if status_code.is_client_error() {
println!("🛑 Client error: {}", status_code);
println!("{:#?}", status_code);
if status_code == 404 {
println!(" Failed to download from {:?}", pkg_spec.download_urls());
}
println!();
if pkg_spec.version == "latest" && pkg_spec.registry == PackageRegistry::Wapm {
jkbecker marked this conversation as resolved.
Show resolved Hide resolved
println!(
"💡 Please note that wapm.io does not properly alias the `{}` version tag.",
"latest".bold().yellow()
);
println!(" You might want to look up the package and explicitly provide its most recent version:");
println!(" \t{}", pkg_spec.profile_url());
println!();
}
// Currently, a 404 is very likely if a package only contains modules that have names other than
// the package name (which the module name defaults to if not provided).
// TODO: retrieve available modules and interactively ask which module should be downloaded
// Quick fix is to point this out to the user:
if pkg_spec.name == pkg_spec.module {
println!(
"💡 Please verify that this package actually contains a `{}` module",
pkg_spec.module.bold().yellow()
);
println!(" by checking the MODULES section on its profile page:");
println!(" \t{}", pkg_spec.profile_url());
println!(
" If the module name differs from the package name, you need to provide it with"
);
println!(
" \tserval pull {}:{}",
pkg_spec.profile_url(),
"<module>".bold().yellow()
);
}
} else {
println!("😵‍💫 Something else happened. Status: {:?}", status_code);
}
}
// Something went horribly wrong.
Err(err) => println!("{:#?}", err),
}
}
Ok(())
}

/// Parse command-line arguments and act.
fn main() -> Result<()> {
let args = Args::parse();
Expand Down Expand Up @@ -320,6 +416,7 @@ fn main() -> Result<()> {
Command::Status { id } => status(id)?,
Command::History => history()?,
Command::Ping => ping()?,
Command::Pull { identifer } => pull(identifer)?,
};

Ok(())
Expand Down
2 changes: 2 additions & 0 deletions utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ tokio = { workspace = true }
toml = "0.7.0"
uuid = { workspace = true }
wasi-common = { workspace = true }
regex = "1.7.1"
sha256 = "1.1.2"
12 changes: 12 additions & 0 deletions utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ pub enum ServalError {
/// Translation for errors from ssri.
#[error("ssri::Error: {0}")]
SsriError(#[from] ssri::Error),

/// The Package Registry is unknown.
#[error("unknown package registry`{0}`")]
PackageRegistryUnknownError(String),

/// The Package Registry Manifest could not be constructed.
#[error("failed to parse registry manifest `{0}`")]
PackageRegistryManifestError(String),

/// The Package Registry Manifest could not be constructed.
#[error("failed to download module from registry: {0}")]
PackageRegistryDownloadError(String),
}

use axum::http::StatusCode;
Expand Down
1 change: 1 addition & 0 deletions utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub mod errors;
pub mod futures;
pub mod mdns;
pub mod networking;
pub mod registry;
pub mod structs;
Loading