diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82efde1..172823f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: - targets: "wasm32-wasi" + targets: "wasm32-wasip1" # We have to run these separately so we can deactivate a feature for one of the tests - name: Run client tests working-directory: ./crates/wasm-pkg-client diff --git a/Cargo.lock b/Cargo.lock index ddf9e9c..7336d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1079,6 +1079,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1295,6 +1301,9 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "hashlink" @@ -2194,8 +2203,8 @@ dependencies = [ "serde_json", "sha2", "tokio", - "wit-component", - "wit-parser", + "wit-component 0.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -4322,6 +4331,16 @@ dependencies = [ "wasmparser 0.221.2", ] +[[package]] +name = "wasm-encoder" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" +dependencies = [ + "leb128", + "wasmparser 0.224.0", +] + [[package]] name = "wasm-metadata" version = "0.219.1" @@ -4338,6 +4357,23 @@ dependencies = [ "wasmparser 0.219.1", ] +[[package]] +name = "wasm-metadata" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79d13d93febc749413cb6f327e4fdba8c84e4d03bd69fcc4a220c66f113c8de1" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "spdx", + "url", + "wasm-encoder 0.224.0", + "wasmparser 0.224.0", +] + [[package]] name = "wasm-pkg-client" version = "0.9.0" @@ -4367,9 +4403,9 @@ dependencies = [ "warg-client", "warg-crypto", "warg-protocol", - "wasm-metadata", + "wasm-metadata 0.224.0", "wasm-pkg-common", - "wit-component", + "wit-component 0.224.0", ] [[package]] @@ -4409,12 +4445,12 @@ dependencies = [ "tokio-util", "toml", "tracing", - "wasm-metadata", + "wasm-metadata 0.224.0", "wasm-pkg-client", "wasm-pkg-common", "windows-sys 0.59.0", - "wit-component", - "wit-parser", + "wit-component 0.224.0", + "wit-parser 0.224.0", ] [[package]] @@ -4465,6 +4501,18 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" +dependencies = [ + "bitflags 2.6.0", + "hashbrown 0.15.2", + "indexmap 2.7.0", + "semver", +] + [[package]] name = "wasmprinter" version = "0.2.80" @@ -4767,9 +4815,28 @@ dependencies = [ "serde_derive", "serde_json", "wasm-encoder 0.219.1", - "wasm-metadata", + "wasm-metadata 0.219.1", "wasmparser 0.219.1", - "wit-parser", + "wit-parser 0.219.1", +] + +[[package]] +name = "wit-component" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad555ab4f4e676474df746d937823c7279c2d6dd36c3e97a61db893d4ef64ee5" +dependencies = [ + "anyhow", + "bitflags 2.6.0", + "indexmap 2.7.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.224.0", + "wasm-metadata 0.224.0", + "wasmparser 0.224.0", + "wit-parser 0.224.0", ] [[package]] @@ -4790,6 +4857,24 @@ dependencies = [ "wasmparser 0.219.1", ] +[[package]] +name = "wit-parser" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e2925a7365d2c6709ae17bdbb5777ffd8154fd70906b413fc01b75f0dba59e" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.7.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.224.0", +] + [[package]] name = "wkg" version = "0.9.0" @@ -4810,7 +4895,7 @@ dependencies = [ "wasm-pkg-client", "wasm-pkg-common", "wasm-pkg-core", - "wit-component", + "wit-component 0.224.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f6786a7..0bd2310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,6 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features = wasm-pkg-common = { version = "0.9.0", path = "crates/wasm-pkg-common" } wasm-pkg-client = { version = "0.9.0", path = "crates/wasm-pkg-client" } wasm-pkg-core = { version = "0.9.0", path = "crates/wasm-pkg-core" } -wasm-metadata = "0.219" -wit-component = "0.219" -wit-parser = "0.219" +wasm-metadata = "0.224" +wit-component = "0.224" +wit-parser = "0.224" diff --git a/crates/wasm-pkg-client/src/oci/publisher.rs b/crates/wasm-pkg-client/src/oci/publisher.rs index 3060c3d..ba8d5d2 100644 --- a/crates/wasm-pkg-client/src/oci/publisher.rs +++ b/crates/wasm-pkg-client/src/oci/publisher.rs @@ -2,7 +2,6 @@ use std::collections::BTreeMap; use oci_client::{Reference, RegistryOperation}; use tokio::io::AsyncReadExt; -use wasm_metadata::LinkType; use crate::publisher::PackagePublisher; use crate::{PackageRef, PublishingSource, Version}; @@ -22,44 +21,39 @@ impl PackagePublisher for OciBackend { // to remove this and use the stream directly. let mut buf = Vec::new(); data.read_to_end(&mut buf).await?; - let meta = wasm_metadata::RegistryMetadata::from_wasm(&buf).map_err(|e| { - crate::Error::InvalidComponent(anyhow::anyhow!("Unable to parse component: {e}")) + let payload = wasm_metadata::Payload::from_binary(&buf).map_err(|e| { + crate::Error::InvalidComponent(anyhow::anyhow!("Unable to parse WASM: {e}")) })?; + let meta = payload.metadata(); let (config, layer) = oci_wasm::WasmConfig::from_raw_component(buf, None) .map_err(crate::Error::InvalidComponent)?; let mut annotations = BTreeMap::from_iter([( "org.opencontainers.image.version".to_string(), version.to_string(), )]); - if let Some(meta) = meta { - if let Some(desc) = meta.get_description() { - annotations.insert( - "org.opencontainers.image.description".to_string(), - desc.to_owned(), - ); - } - if let Some(licenses) = meta.get_license() { - annotations.insert( - "org.opencontainers.image.licenses".to_string(), - licenses.to_owned(), - ); - } - if let Some(sources) = meta.get_links() { - for link in sources { - if link.ty == LinkType::Repository { - annotations.insert( - "org.opencontainers.image.source".to_string(), - link.value.to_owned(), - ); - } - if link.ty == LinkType::Homepage { - annotations.insert( - "org.opencontainers.image.url".to_string(), - link.value.to_owned(), - ); - } - } - } + if let Some(desc) = &meta.description { + annotations.insert( + "org.opencontainers.image.description".to_string(), + desc.to_string(), + ); + } + if let Some(licenses) = &meta.licenses { + annotations.insert( + "org.opencontainers.image.licenses".to_string(), + licenses.to_string(), + ); + } + if let Some(source) = &meta.source { + annotations.insert( + "org.opencontainers.image.source".to_string(), + source.to_string(), + ); + } + if let Some(homepage) = &meta.homepage { + annotations.insert( + "org.opencontainers.image.url".to_string(), + homepage.to_string(), + ); } let reference: Reference = self.make_reference(package, Some(version)); diff --git a/crates/wasm-pkg-core/src/config.rs b/crates/wasm-pkg-core/src/config.rs index fec5753..be6df51 100644 --- a/crates/wasm-pkg-core/src/config.rs +++ b/crates/wasm-pkg-core/src/config.rs @@ -9,7 +9,6 @@ use anyhow::{Context, Result}; use semver::VersionReq; use serde::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; -use wasm_metadata::{Link, LinkType, RegistryMetadata}; /// The default name of the configuration file. pub const CONFIG_FILE_NAME: &str = "wkg.toml"; @@ -17,6 +16,7 @@ pub const CONFIG_FILE_NAME: &str = "wkg.toml"; /// The structure for a wkg.toml configuration file. This file is entirely optional and is used for /// overriding and annotating wasm packages. #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] +#[serde(deny_unknown_fields)] pub struct Config { /// Overrides for various packages #[serde(default, skip_serializing_if = "Option::is_none")] @@ -60,6 +60,7 @@ impl Config { } #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] +#[serde(deny_unknown_fields)] pub struct Override { /// A path to the package on disk. If this is set, the package will be loaded from the given /// path. If this is not set, the package will be loaded from the registry. @@ -72,59 +73,26 @@ pub struct Override { } #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] +#[serde(deny_unknown_fields)] pub struct Metadata { - /// The authors of the package. + /// The author(s) of the package. #[serde(default, skip_serializing_if = "Option::is_none")] - pub authors: Option>, - /// The categories of the package. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub categories: Option>, + pub author: Option, /// The package description. #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, /// The package license. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub license: Option, - /// The package documentation URL. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub documentation: Option, + #[serde(default, skip_serializing_if = "Option::is_none", alias = "license")] + pub licenses: Option, + /// The package source code URL. + #[serde(default, skip_serializing_if = "Option::is_none", alias = "repository")] + pub source: Option, /// The package homepage URL. #[serde(default, skip_serializing_if = "Option::is_none")] pub homepage: Option, - /// The package repository URL. + /// The package source control revision. #[serde(default, skip_serializing_if = "Option::is_none")] - pub repository: Option, -} - -impl From for wasm_metadata::RegistryMetadata { - fn from(value: Metadata) -> Self { - let mut meta = RegistryMetadata::default(); - meta.set_authors(value.authors); - meta.set_categories(value.categories); - meta.set_description(value.description); - meta.set_license(value.license); - let mut links = Vec::new(); - if let Some(documentation) = value.documentation { - links.push(Link { - ty: LinkType::Documentation, - value: documentation, - }); - } - if let Some(homepage) = value.homepage { - links.push(Link { - ty: LinkType::Homepage, - value: homepage, - }); - } - if let Some(repository) = value.repository { - links.push(Link { - ty: LinkType::Repository, - value: repository, - }); - } - meta.set_links((!links.is_empty()).then_some(links)); - meta - } + pub revision: Option, } #[cfg(test)] @@ -144,13 +112,12 @@ mod tests { }, )])), metadata: Some(Metadata { - authors: Some(vec!["foo".to_string(), "bar".to_string()]), - categories: Some(vec!["foo".to_string(), "bar".to_string()]), - description: Some("foo".to_string()), - license: Some("foo".to_string()), - documentation: Some("foo".to_string()), - homepage: Some("foo".to_string()), - repository: Some("foo".to_string()), + author: Some("Foo Bar".to_string()), + description: Some("Foobar baz".to_string()), + licenses: Some("FBB".to_string()), + source: Some("https://gitfoo/bar".to_string()), + homepage: Some("https://foo.bar".to_string()), + revision: Some("f00ba4".to_string()), }), }; diff --git a/crates/wasm-pkg-core/src/lock.rs b/crates/wasm-pkg-core/src/lock.rs index 1bb5a00..93a41b8 100644 --- a/crates/wasm-pkg-core/src/lock.rs +++ b/crates/wasm-pkg-core/src/lock.rs @@ -606,7 +606,7 @@ mod sys { } pub(super) fn error_contended(err: &Error) -> bool { - err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK) + err.raw_os_error() == Some(libc::EWOULDBLOCK) } pub(super) fn error_unsupported(err: &Error) -> bool { diff --git a/crates/wasm-pkg-core/src/wit.rs b/crates/wasm-pkg-core/src/wit.rs index 059cef1..ed5eac2 100644 --- a/crates/wasm-pkg-core/src/wit.rs +++ b/crates/wasm-pkg-core/src/wit.rs @@ -4,6 +4,7 @@ use std::{collections::HashSet, path::Path, str::FromStr}; use anyhow::{Context, Result}; use semver::{Version, VersionReq}; +use wasm_metadata::AddMetadata; use wasm_pkg_client::{ caching::{CachingClient, FileCache}, PackageRef, @@ -58,6 +59,7 @@ pub async fn build_package( lock_file.update_dependencies(&dependencies); let (resolve, pkg_id) = dependencies.generate_resolve(wit_dir).await?; + let bytes = wit_component::encode(&resolve, pkg_id)?; let pkg = &resolve.packages[pkg_id]; let name = PackageRef::new( @@ -68,26 +70,35 @@ pub async fn build_package( pkg.name .name .parse() - .context("Invalid name found for package")?, - ); - - let bytes = wit_component::encode(&resolve, pkg_id)?; - - let mut producers = wasm_metadata::Producers::empty(); - producers.add( - "processed-by", - env!("CARGO_PKG_NAME"), - option_env!("WIT_VERSION_INFO").unwrap_or(env!("CARGO_PKG_VERSION")), + .context("Invalid name found in package")?, ); + let version = pkg + .name + .version + .as_ref() + .map(|v| v.to_string().parse()) + .transpose() + .context("Invalid version found in package")?; - let mut bytes = producers - .add_to_wasm(&bytes) - .context("failed to add producers metadata to output WIT package")?; + let processed_by_version = option_env!("WIT_VERSION_INFO").unwrap_or(env!("CARGO_PKG_VERSION")); - if let Some(meta) = config.metadata.clone() { - let meta = wasm_metadata::RegistryMetadata::from(meta); - bytes = meta.add_to_wasm(&bytes)?; - } + let metadata = config.metadata.clone().unwrap_or_default(); + let add_metadata = AddMetadata { + name: Some(format!("{}:{}", pkg.name.namespace, pkg.name.name)), + processed_by: vec![( + env!("CARGO_PKG_NAME").to_string(), + processed_by_version.to_string(), + )], + author: metadata.author.map(|v| v.parse()).transpose()?, + description: metadata.description.map(|v| v.parse()).transpose()?, + licenses: metadata.licenses.map(|v| v.parse()).transpose()?, + source: metadata.source.map(|v| v.parse()).transpose()?, + homepage: metadata.homepage.map(|v| v.parse()).transpose()?, + revision: metadata.revision.map(|v| v.parse()).transpose()?, + version, + ..Default::default() + }; + let bytes = add_metadata.to_wasm(&bytes)?; Ok((name, pkg.name.version.clone(), bytes)) } @@ -345,10 +356,10 @@ async fn print_wit_from_resolve( let dep_path = root_deps_dir.join(name_from_package_name(&pkg.name)); tokio::fs::create_dir_all(&dep_path).await?; let mut printer = WitPrinter::default(); - let wit = printer + printer .print(resolve, id, &[]) .context("Unable to print wit")?; - tokio::fs::write(dep_path.join("package.wit"), wit).await?; + tokio::fs::write(dep_path.join("package.wit"), &printer.output.to_string()).await?; } Ok(()) } diff --git a/crates/wasm-pkg-core/tests/fixtures/cli-example/.cargo/config.toml b/crates/wasm-pkg-core/tests/fixtures/cli-example/.cargo/config.toml index 12dc204..e5de4ea 100644 --- a/crates/wasm-pkg-core/tests/fixtures/cli-example/.cargo/config.toml +++ b/crates/wasm-pkg-core/tests/fixtures/cli-example/.cargo/config.toml @@ -1,2 +1,2 @@ [build] -target = [ "wasm32-wasi" ] +target = [ "wasm32-wasip1" ] diff --git a/crates/wasm-pkg-core/tests/fixtures/dog-fetcher/.cargo/config.toml b/crates/wasm-pkg-core/tests/fixtures/dog-fetcher/.cargo/config.toml index 12dc204..e5de4ea 100644 --- a/crates/wasm-pkg-core/tests/fixtures/dog-fetcher/.cargo/config.toml +++ b/crates/wasm-pkg-core/tests/fixtures/dog-fetcher/.cargo/config.toml @@ -1,2 +1,2 @@ [build] -target = [ "wasm32-wasi" ] +target = [ "wasm32-wasip1" ] diff --git a/crates/wkg/src/main.rs b/crates/wkg/src/main.rs index a2ec5d2..b98fccc 100644 --- a/crates/wkg/src/main.rs +++ b/crates/wkg/src/main.rs @@ -354,7 +354,9 @@ impl GetArgs { match wit_component::decode_reader(&mut file) { Ok(DecodedWasm::WitPackage(resolve, pkg)) => { tracing::debug!(?pkg, "decoded WIT package"); - Some(wit_component::WitPrinter::default().print(&resolve, pkg, &[])?) + let mut printer = wit_component::WitPrinter::default(); + printer.print(&resolve, pkg, &[])?; + Some(printer.output.to_string()) } Ok(_) => None, Err(err) => { diff --git a/crates/wkg/tests/e2e.rs b/crates/wkg/tests/e2e.rs index 89d7d6b..cb407ae 100644 --- a/crates/wkg/tests/e2e.rs +++ b/crates/wkg/tests/e2e.rs @@ -73,12 +73,12 @@ async fn build_and_publish_with_metadata() { ); assert_eq!( annotations.get("org.opencontainers.image.licenses"), - meta.license.as_ref(), + meta.licenses.as_ref(), "License should match" ); assert_eq!( annotations.get("org.opencontainers.image.source"), - meta.repository.as_ref(), + meta.source.as_ref(), "Source should match" ); assert_eq!( diff --git a/crates/wkg/tests/fixtures/wasi-http/wkg.toml b/crates/wkg/tests/fixtures/wasi-http/wkg.toml index 7625d3a..9f61dc9 100644 --- a/crates/wkg/tests/fixtures/wasi-http/wkg.toml +++ b/crates/wkg/tests/fixtures/wasi-http/wkg.toml @@ -1,8 +1,7 @@ [metadata] -authors = ["WasmPkg "] -categories = ["wasm-pkg"] +author = "WasmPkg " description = "WASI HTTP interface" license = "Apache-2.0" -documentation = "https://docs.foobar.baz" -homepage = "https://foobar.baz" -repository = "https://github.com/bytecodealliance/wasm-pkg-tools" +source = "https://github.com/bytecodealliance/wasm-pkg-tools" +homepage = "https://foobar.baz/" +revision = "abcd123"