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

feat(cli): Restore packages from webcs #5089

Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 19 additions & 5 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ members = [
"lib/derive",
"lib/emscripten",
"lib/object",
"lib/package",
"lib/registry",
"lib/sys-utils",
"lib/types",
Expand Down Expand Up @@ -109,6 +110,7 @@ toml = {version = "0.5.9", features = ["preserve_order"]}
indexmap = "2"
serde_yaml = "0.9.34"
libc = { version = "^0.2", default-features = false }
pretty_assertions = "1.4.0"
base64 = "0.22.0"

[build-dependencies]
Expand Down
4 changes: 3 additions & 1 deletion lib/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ wasmer-compiler-cranelift = { version = "=4.3.7", path = "../compiler-cranelift"
wasmer-compiler-singlepass = { version = "=4.3.7", path = "../compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "=4.3.7", path = "../compiler-llvm", optional = true }
wasmer-emscripten = { version = "=4.3.7", path = "../emscripten" }
wasmer-package = { version = "=0.1.0", path = "../package" }

wasmer-vm = { version = "=4.3.7", path = "../vm", optional = true }
wasmer-wasix = { path = "../wasix", version = "=0.27.0", features = [
"logging",
Expand Down Expand Up @@ -276,7 +278,7 @@ unix_mode = "0.1.3"
[dev-dependencies]
assert_cmd = "2.0.11"
predicates = "3.0.3"
pretty_assertions = "1.3.0"
pretty_assertions.workspace = true

[target.'cfg(target_os = "windows")'.dependencies]
colored = "2.0.0"
Expand Down
45 changes: 40 additions & 5 deletions lib/cli/src/commands/package/unpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,53 @@ use dialoguer::console::{style, Emoji};
use indicatif::ProgressBar;

/// Extract contents of a webc image to a directory.
///
/// See --format flag for available output formats.
#[derive(clap::Parser, Debug)]
pub struct PackageUnpack {
/// The output directory.
#[clap(short = 'o', long)]
out_dir: PathBuf,
pub out_dir: PathBuf,

/// Overwrite existing directories/files.
#[clap(long)]
overwrite: bool,
pub overwrite: bool,

/// Run the unpack command without any output
#[clap(long)]
pub quiet: bool,

/// Path to the package.
package_path: PathBuf,
pub package_path: PathBuf,

/// Output format.
///
/// * package
/// Restore a package directory with a wasmer.toml
/// NOTE: this conversion is lossy, because webcs don't store the original
/// wasmer.toml and the full contents can not be restored.
///
/// * webc
/// Directly unpack the webc contents.
/// - Volumes will be placed in subdirectories.
/// - atoms will be placed in the root directory
/// - the full webc manifest will be placed in a manifest.json file
#[clap(short, long, default_value = "package")]
pub format: Format,
}

static PACKAGE_EMOJI: Emoji<'_, '_> = Emoji("📦 ", "");
static EXTRACTED_TO_EMOJI: Emoji<'_, '_> = Emoji("📂 ", "");

/// Webc unpack format.
#[derive(clap::ValueEnum, Clone, Debug)]
pub enum Format {
/// See [`PackageUnpack::format`] for details.
Package,
/// See [`PackageUnpack::format`] for details.
Webc,
}

impl PackageUnpack {
pub(crate) fn execute(&self) -> Result<(), anyhow::Error> {
// Setup the progress bar
Expand All @@ -52,8 +78,16 @@ impl PackageUnpack {
std::fs::create_dir_all(outdir)
.with_context(|| format!("could not create output directory '{}'", outdir.display()))?;

pkg.unpack(outdir, self.overwrite)
.with_context(|| "could not extract package".to_string())?;
match self.format {
Format::Package => {
wasmer_package::convert::webc_to_package_dir(&pkg, outdir)
.with_context(|| "could not extract package")?;
}
Format::Webc => {
pkg.unpack(outdir, self.overwrite)
.with_context(|| "could not extract package".to_string())?;
}
}

pb.println(format!(
"{} {}Extracted package contents to '{}'",
Expand Down Expand Up @@ -89,6 +123,7 @@ mod tests {
overwrite: false,
package_path,
quiet: true,
format: Format::Webc,
};

cmd.execute().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion lib/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ hex = "0.4.3"
ciborium = "0.2.2"

[dev-dependencies]
pretty_assertions = "1.4.0"
pretty_assertions.workspace = true
serde_json = "1.0.116"
tempfile = "3.3.0"
14 changes: 14 additions & 0 deletions lib/config/src/package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ pub struct Package {
}

impl Package {
pub fn new_empty() -> Self {
PackageBuilder::default().build().unwrap()
}

/// Create a [`PackageBuilder`] populated with all mandatory fields.
pub fn builder(
name: impl Into<String>,
Expand Down Expand Up @@ -732,6 +736,16 @@ pub struct Manifest {
}

impl Manifest {
pub fn new_empty() -> Self {
Self {
package: None,
dependencies: HashMap::new(),
fs: IndexMap::new(),
modules: Vec::new(),
commands: Vec::new(),
}
}

/// Create a [`ManifestBuilder`] populated with all mandatory fields.
pub fn builder(package: Package) -> ManifestBuilder {
ManifestBuilder::new(package)
Expand Down
21 changes: 21 additions & 0 deletions lib/package/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "wasmer-package"
version = "0.1.0"

authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
webc.workspace = true
wasmer-config = { version = "0.8.0", path = "../config" }
toml = "0.8.0"

[dev-dependencies]
pretty_assertions.workspace = true
tempfile = "3.12.0"
47 changes: 47 additions & 0 deletions lib/package/src/convert/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct ConversionError {
message: String,
cause: Option<Arc<dyn std::error::Error + Send + Sync>>,
}

impl ConversionError {
pub fn msg(msg: impl Into<String>) -> Self {
Self {
message: msg.into(),
cause: None,
}
}

pub fn with_cause(
msg: impl Into<String>,
cause: impl std::error::Error + Send + Sync + 'static,
) -> Self {
Self {
message: msg.into(),
cause: Some(Arc::new(cause)),
}
}
}

impl std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "could not convert manifest: {}", self.message)?;
if let Some(cause) = &self.cause {
write!(f, " (cause: {})", cause)?;
}

Ok(())
}
}

impl std::error::Error for ConversionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Some(e) = &self.cause {
Some(e)
} else {
None
}
}
}
4 changes: 4 additions & 0 deletions lib/package/src/convert/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod error;
mod webc_to_package;

pub use self::{error::ConversionError, webc_to_package::webc_to_package_dir};
Loading
Loading