Skip to content

Commit 91b374d

Browse files
authored
Merge pull request #163 from bytecodealliance/common-wasm32
Trim wasm-pkg-common to allow targetting wasm32-wasip1
2 parents 5221bdc + 29c681f commit 91b374d

File tree

15 files changed

+154
-140
lines changed

15 files changed

+154
-140
lines changed

.github/workflows/ci.yml

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,34 @@ jobs:
1010
matrix:
1111
include:
1212
- os: ubuntu-latest
13-
additional_test_flags: ""
13+
run_lints: true
14+
test_wasm_build: true
1415
- os: windows-latest
15-
additional_test_flags: "--no-default-features"
16+
docker_tests_flags: "--no-default-features"
1617
- os: macos-latest
17-
additional_test_flags: "--no-default-features"
18+
docker_tests_flags: "--no-default-features"
1819
steps:
1920
- uses: actions/checkout@v4
2021
- uses: dtolnay/rust-toolchain@stable
2122
with:
2223
targets: "wasm32-wasip1"
23-
# We have to run these separately so we can deactivate a feature for one of the tests
24-
- name: Run client tests
25-
working-directory: ./crates/wasm-pkg-client
26-
run: cargo test ${{ matrix.additional_test_flags }}
27-
- name: Run wkg tests
28-
working-directory: ./crates/wkg
29-
run: cargo test ${{ matrix.additional_test_flags }}
30-
- name: Run other tests
24+
25+
- name: Run lints
26+
if: matrix.run_lints
27+
run: |
28+
cargo clippy --workspace --no-default-features
29+
cargo clippy --workspace --all-features
30+
31+
- name: Run tests
3132
run: cargo test --workspace --exclude wasm-pkg-client --exclude wkg
32-
- name: Run cargo clippy
33-
run: cargo clippy --all --workspace
33+
34+
# NOTE: Docker tests are only run on linux because other platforms haven't
35+
# always worked consistently.
36+
- name: Run wasm-pkg-client tests
37+
run: cargo test -p wasm-pkg-client ${{ matrix.docker_tests_flags }}
38+
- name: Run wkg tests
39+
run: cargo test -p wkg ${{ matrix.docker_tests_flags }}
40+
41+
- name: Test wasm32-wasip1 build for wasm-pkg-common
42+
if: matrix.test_wasm_build
43+
run: cargo build -p wasm-pkg-common --target wasm32-wasip1

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/wasm-pkg-client/Cargo.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ license.workspace = true
99
readme = "../../README.md"
1010

1111
[features]
12-
default = ["_local"]
13-
# An internal feature for making sure e2e tests can run locally but not in CI for Mac or Windows
14-
_local = []
12+
default = ["docker-tests"]
13+
# This feature enables tests that use Docker
14+
docker-tests = []
1515

1616
[dependencies]
1717
anyhow = { workspace = true }
@@ -23,6 +23,13 @@ etcetera = { workspace = true }
2323
futures-util = { workspace = true, features = ["io"] }
2424
oci-client = { workspace = true }
2525
oci-wasm = { workspace = true }
26+
reqwest = { version = "0.12.0", default-features = false, features = [
27+
"charset",
28+
"http2",
29+
"json",
30+
"macos-system-configuration",
31+
"rustls-tls",
32+
]}
2633
secrecy = { version = "0.8", features = ["serde"] }
2734
serde = { workspace = true }
2835
serde_json = { workspace = true }
@@ -38,7 +45,7 @@ warg-client = "0.9.2"
3845
warg-crypto = "0.9.2"
3946
wasm-metadata = { workspace = true }
4047
warg-protocol = "0.9.2"
41-
wasm-pkg-common = { workspace = true, features = ["metadata-client", "tokio"] }
48+
wasm-pkg-common = { workspace = true, features = ["registry-config"] }
4249
wit-component = { workspace = true }
4350

4451
[dev-dependencies]

crates/wasm-pkg-client/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
pub mod caching;
3030
mod loader;
3131
pub mod local;
32+
pub mod metadata;
3233
pub mod oci;
3334
mod publisher;
3435
mod release;
@@ -55,6 +56,7 @@ pub use wasm_pkg_common::{
5556
};
5657
use wit_component::DecodedWasm;
5758

59+
use crate::metadata::RegistryMetadataExt;
5860
use crate::{loader::PackageLoader, local::LocalBackend, oci::OciBackend, warg::WargBackend};
5961

6062
pub use release::{Release, VersionInfo};

crates/wasm-pkg-client/src/local.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
//!
33
//! Each package release is a file: `<root>/<namespace>/<name>/<version>.wasm`
44
5-
use std::path::PathBuf;
5+
use std::path::{Path, PathBuf};
66

77
use anyhow::anyhow;
88
use async_trait::async_trait;
99
use futures_util::{StreamExt, TryStreamExt};
1010
use serde::Deserialize;
11+
use sha2::{Digest, Sha256};
1112
use tokio_util::io::ReaderStream;
1213
use wasm_pkg_common::{
1314
config::RegistryConfig,
@@ -85,7 +86,7 @@ impl PackageLoader for LocalBackend {
8586
async fn get_release(&self, package: &PackageRef, version: &Version) -> Result<Release, Error> {
8687
let path = self.version_path(package, version);
8788
tracing::debug!(path = %path.display(), "Reading content from path");
88-
let content_digest = ContentDigest::sha256_from_file(path).await?;
89+
let content_digest = sha256_from_file(path).await?;
8990
Ok(Release {
9091
version: version.clone(),
9192
content_digest,
@@ -123,3 +124,18 @@ impl PackagePublisher for LocalBackend {
123124
.map(|_| ())
124125
}
125126
}
127+
128+
async fn sha256_from_file(path: impl AsRef<Path>) -> Result<ContentDigest, std::io::Error> {
129+
use tokio::io::AsyncReadExt;
130+
let mut file = tokio::fs::File::open(path).await?;
131+
let mut hasher = Sha256::new();
132+
let mut buf = [0; 4096];
133+
loop {
134+
let n = file.read(&mut buf).await?;
135+
if n == 0 {
136+
break;
137+
}
138+
hasher.update(&buf[..n]);
139+
}
140+
Ok(hasher.into())
141+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use anyhow::Context;
2+
use reqwest::StatusCode;
3+
use wasm_pkg_common::{
4+
metadata::{RegistryMetadata, REGISTRY_METADATA_PATH},
5+
registry::Registry,
6+
Error,
7+
};
8+
9+
/// Extension trait for [`RegistryMetadata`] adding client functionality.
10+
pub trait RegistryMetadataExt: Sized {
11+
/// Attempt to fetch [`RegistryMetadata`] from the given [`Registry`]. On
12+
/// failure, return defaults.
13+
fn fetch_or_default(registry: &Registry) -> impl std::future::Future<Output = Self> + Send;
14+
15+
/// Fetch [`RegistryMetadata`] from the given [`Registry`].
16+
fn fetch(
17+
registry: &Registry,
18+
) -> impl std::future::Future<Output = Result<Option<Self>, Error>> + Send;
19+
}
20+
21+
impl RegistryMetadataExt for RegistryMetadata {
22+
async fn fetch_or_default(registry: &Registry) -> Self {
23+
match Self::fetch(registry).await {
24+
Ok(Some(meta)) => {
25+
tracing::debug!(?meta, "Got registry metadata");
26+
meta
27+
}
28+
Ok(None) => {
29+
tracing::debug!("Metadata not found");
30+
Default::default()
31+
}
32+
Err(err) => {
33+
tracing::warn!(error = ?err, "Error fetching registry metadata");
34+
Default::default()
35+
}
36+
}
37+
}
38+
39+
async fn fetch(registry: &Registry) -> Result<Option<Self>, Error> {
40+
let scheme = if registry.host() == "localhost" {
41+
"http"
42+
} else {
43+
"https"
44+
};
45+
let url = format!("{scheme}://{registry}{REGISTRY_METADATA_PATH}");
46+
fetch_url(&url)
47+
.await
48+
.with_context(|| format!("error fetching registry metadata from {url:?}"))
49+
.map_err(Error::RegistryMetadataError)
50+
}
51+
}
52+
53+
async fn fetch_url(url: &str) -> anyhow::Result<Option<RegistryMetadata>> {
54+
tracing::debug!(?url, "Fetching registry metadata");
55+
56+
let resp = reqwest::get(url).await?;
57+
if resp.status() == StatusCode::NOT_FOUND {
58+
return Ok(None);
59+
}
60+
let resp = resp.error_for_status()?;
61+
Ok(Some(resp.json().await?))
62+
}

crates/wasm-pkg-client/tests/e2e.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
use futures_util::TryStreamExt;
2-
use testcontainers::{
3-
core::{IntoContainerPort, WaitFor},
4-
runners::AsyncRunner,
5-
GenericImage, ImageExt,
6-
};
72
use wasm_pkg_client::{Client, Config};
83

94
const FIXTURE_WASM: &str = "./tests/testdata/binary_wit.wasm";
105

11-
#[cfg(any(target_os = "linux", feature = "_local"))]
12-
// NOTE: These are only run on linux for CI purposes, because they rely on the docker client being
13-
// available, and for various reasons this has proven to be problematic on both the Windows and
14-
// MacOS runners due to it not being installed (yay licensing).
6+
#[cfg(feature = "docker-tests")]
157
#[tokio::test]
168
async fn publish_and_fetch_smoke_test() {
9+
use testcontainers::{
10+
core::{IntoContainerPort, WaitFor},
11+
runners::AsyncRunner,
12+
GenericImage, ImageExt,
13+
};
14+
1715
let _container = GenericImage::new("registry", "2")
1816
.with_wait_for(WaitFor::message_on_stderr("listening on [::]:5000"))
1917
.with_mapped_port(5001, 5000.tcp())

crates/wasm-pkg-common/Cargo.toml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,27 @@ license.workspace = true
99
readme = "../../README.md"
1010

1111
[features]
12-
metadata-client = ["dep:reqwest"]
13-
tokio = ["tokio/io-util"]
12+
registry-config = [
13+
"dep:etcetera",
14+
"dep:tokio",
15+
"dep:toml",
16+
]
1417
# Extra features to facilitate making working with OCI images easier
1518
oci_extras = []
1619

1720
[dependencies]
1821
anyhow = { workspace = true }
1922
bytes = { workspace = true }
20-
etcetera = { workspace = true }
23+
etcetera = { workspace = true, optional = true }
2124
futures-util = { workspace = true }
2225
http = "1.1.0"
23-
reqwest = { version = "0.12.0", default-features = false, features = [
24-
"rustls-tls",
25-
"charset",
26-
"http2",
27-
"macos-system-configuration",
28-
"json",
29-
], optional = true }
3026
semver = { workspace = true }
3127
serde = { workspace = true }
3228
serde_json = { workspace = true }
3329
sha2 = { workspace = true }
34-
tokio = { workspace = true, features = ["fs"] }
35-
toml = { workspace = true }
30+
tokio = { workspace = true, optional = true, features = ["fs"] }
31+
toml = { workspace = true, optional = true }
3632
thiserror = { workspace = true }
37-
tracing = { workspace = true }
3833

3934
[dev-dependencies]
4035
tokio = { workspace = true, features = ["macros", "rt"] }

crates/wasm-pkg-common/src/config.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::{
44
path::{Path, PathBuf},
55
};
66

7-
use etcetera::BaseStrategy;
87
use serde::{Deserialize, Serialize};
98

109
use crate::{
@@ -103,13 +102,6 @@ impl Config {
103102
Ok(config)
104103
}
105104

106-
/// Returns the default global config file location
107-
pub fn global_config_path() -> Option<PathBuf> {
108-
etcetera::choose_base_strategy()
109-
.ok()
110-
.map(|strat| strat.config_dir().join("wasm-pkg").join("config.toml"))
111-
}
112-
113105
/// Reads config from the default global config file location
114106
pub async fn read_global_config() -> Result<Option<Self>, Error> {
115107
let path = match Config::global_config_path() {
@@ -124,6 +116,14 @@ impl Config {
124116
Ok(Some(Self::from_toml(&contents)?))
125117
}
126118

119+
/// Returns the default global config file location
120+
pub fn global_config_path() -> Option<PathBuf> {
121+
use etcetera::BaseStrategy;
122+
etcetera::choose_base_strategy()
123+
.ok()
124+
.map(|strat| strat.config_dir().join("wasm-pkg").join("config.toml"))
125+
}
126+
127127
/// Reads config from a TOML file at the given path.
128128
pub async fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
129129
let contents = tokio::fs::read_to_string(path)
@@ -134,14 +134,13 @@ impl Config {
134134

135135
/// Parses config from the given TOML contents.
136136
pub fn from_toml(contents: &str) -> Result<Self, Error> {
137-
let toml_cfg: toml::TomlConfig =
138-
::toml::from_str(contents).map_err(Error::invalid_config)?;
137+
let toml_cfg: toml::TomlConfig = ::toml::from_str(contents).map_err(invalid_config)?;
139138
Ok(toml_cfg.into())
140139
}
141140

142141
/// Writes the config to a TOML file at the given path.
143142
pub async fn to_file(&self, path: impl AsRef<Path>) -> Result<(), Error> {
144-
let toml_str = ::toml::to_string(&self).map_err(Error::invalid_config)?;
143+
let toml_str = ::toml::to_string(&self).map_err(invalid_config)?;
145144
tokio::fs::write(path, toml_str)
146145
.await
147146
.map_err(Error::ConfigFileIoError)
@@ -330,7 +329,7 @@ impl RegistryConfig {
330329
let Some(table) = self.backend_configs.get(backend_type) else {
331330
return Ok(None);
332331
};
333-
let config = table.clone().try_into().map_err(Error::invalid_config)?;
332+
let config = table.clone().try_into().map_err(invalid_config)?;
334333
Ok(Some(config))
335334
}
336335

@@ -340,7 +339,7 @@ impl RegistryConfig {
340339
backend_type: impl Into<String>,
341340
backend_config: T,
342341
) -> Result<(), Error> {
343-
let table = ::toml::Table::try_from(backend_config).map_err(Error::invalid_config)?;
342+
let table = ::toml::Table::try_from(backend_config).map_err(invalid_config)?;
344343
self.backend_configs.insert(backend_type.into(), table);
345344
Ok(())
346345
}
@@ -368,3 +367,7 @@ impl std::fmt::Debug for DebugBackendConfigs<'_> {
368367
.finish()
369368
}
370369
}
370+
371+
fn invalid_config(err: impl Into<anyhow::Error>) -> Error {
372+
Error::InvalidConfig(err.into())
373+
}

0 commit comments

Comments
 (0)