Skip to content

Commit 51a09f4

Browse files
committed
Load target env from other registry or local dir
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent 531e4fb commit 51a09f4

File tree

4 files changed

+102
-12
lines changed

4 files changed

+102
-12
lines changed

crates/build/src/manifest.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use spin_manifest::{schema::v2, ManifestVersion};
77
pub enum ManifestBuildInfo {
88
Loadable {
99
components: Vec<ComponentBuildInfo>,
10-
deployment_targets: Vec<String>,
10+
deployment_targets: Vec<spin_manifest::schema::v2::TargetEnvironmentRef>,
1111
manifest: spin_manifest::schema::v2::AppManifest,
1212
},
1313
Unloadable {
@@ -32,7 +32,7 @@ impl ManifestBuildInfo {
3232
}
3333
}
3434

35-
pub fn deployment_targets(&self) -> &[String] {
35+
pub fn deployment_targets(&self) -> &[spin_manifest::schema::v2::TargetEnvironmentRef] {
3636
match self {
3737
Self::Loadable {
3838
deployment_targets, ..
@@ -113,7 +113,7 @@ fn build_configs_from_manifest(
113113

114114
fn deployment_targets_from_manifest(
115115
manifest: &spin_manifest::schema::v2::AppManifest,
116-
) -> Vec<String> {
116+
) -> Vec<spin_manifest::schema::v2::TargetEnvironmentRef> {
117117
manifest.application.targets.clone()
118118
}
119119

crates/environments/src/environment_definition.rs

+75-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
1+
use std::path::Path;
2+
13
use anyhow::Context;
4+
use spin_common::ui::quoted_path;
5+
use spin_manifest::schema::v2::TargetEnvironmentRef;
6+
7+
const DEFAULT_REGISTRY: &str = "fermyon.com";
28

39
/// Loads the given `TargetEnvironment` from a registry.
4-
pub async fn load_environment(env_id: impl AsRef<str>) -> anyhow::Result<TargetEnvironment> {
5-
use futures_util::TryStreamExt;
10+
pub async fn load_environment(env_id: &TargetEnvironmentRef) -> anyhow::Result<TargetEnvironment> {
11+
match env_id {
12+
TargetEnvironmentRef::DefaultRegistry(package) => {
13+
load_environment_from_registry(DEFAULT_REGISTRY, package).await
14+
}
15+
TargetEnvironmentRef::Registry { registry, package } => {
16+
load_environment_from_registry(registry, package).await
17+
}
18+
TargetEnvironmentRef::WitDirectory { path } => load_environment_from_dir(path),
19+
}
20+
}
621

7-
let env_id = env_id.as_ref();
22+
async fn load_environment_from_registry(
23+
registry: &str,
24+
env_id: &str,
25+
) -> anyhow::Result<TargetEnvironment> {
26+
use futures_util::TryStreamExt;
827

928
let (pkg_name, pkg_ver) = env_id.split_once('@').with_context(|| format!("Failed to parse target environment {env_id} as package reference - is the target correct?"))?;
29+
let env_pkg_ref: wasm_pkg_loader::PackageRef = pkg_name
30+
.parse()
31+
.with_context(|| format!("Environment {pkg_name} is not a valid package name"))?;
32+
33+
let registry: wasm_pkg_loader::Registry = registry
34+
.parse()
35+
.with_context(|| format!("Registry {registry} is not a valid registry name"))?;
1036

1137
// TODO: this requires wkg configuration which shouldn't be on users:
1238
// is there a better way to handle it?
13-
let mut client = wasm_pkg_loader::Client::with_global_defaults()
14-
.context("Failed to create a package loader from your global settings")?;
39+
let mut wkg_config = wasm_pkg_loader::Config::global_defaults()
40+
.unwrap_or_else(|_| wasm_pkg_loader::Config::empty());
41+
wkg_config.set_package_registry_override(env_pkg_ref, registry);
42+
43+
let mut client = wasm_pkg_loader::Client::new(wkg_config);
1544

1645
let package = pkg_name
1746
.to_owned()
@@ -35,7 +64,14 @@ pub async fn load_environment(env_id: impl AsRef<str>) -> anyhow::Result<TargetE
3564
.with_context(|| format!("Failed to get {env_id} package data from registry"))?
3665
.to_vec();
3766

38-
TargetEnvironment::new(env_id.to_owned(), bytes)
67+
TargetEnvironment::from_package_bytes(env_id.to_owned(), bytes)
68+
}
69+
70+
fn load_environment_from_dir(path: &Path) -> anyhow::Result<TargetEnvironment> {
71+
let mut resolve = wit_parser::Resolve::default();
72+
let (pkg_id, _) = resolve.push_dir(path)?;
73+
let decoded = wit_parser::decoding::DecodedWasm::WitPackage(resolve, pkg_id);
74+
TargetEnvironment::from_decoded_wasm(path, decoded)
3975
}
4076

4177
/// A parsed document representing a deployment environment, e.g. Spin 2.7,
@@ -57,7 +93,7 @@ pub struct TargetEnvironment {
5793
}
5894

5995
impl TargetEnvironment {
60-
fn new(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
96+
fn from_package_bytes(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
6197
let decoded = wit_component::decode(&bytes)
6298
.with_context(|| format!("Failed to decode package for environment {name}"))?;
6399
let package_id = decoded.package();
@@ -79,6 +115,38 @@ impl TargetEnvironment {
79115
})
80116
}
81117

118+
fn from_decoded_wasm(
119+
source: &Path,
120+
decoded: wit_parser::decoding::DecodedWasm,
121+
) -> anyhow::Result<Self> {
122+
let package_id = decoded.package();
123+
let package = decoded
124+
.resolve()
125+
.packages
126+
.get(package_id)
127+
.with_context(|| {
128+
format!(
129+
"The {} environment is invalid (no package for decoded package ID)",
130+
quoted_path(source)
131+
)
132+
})?
133+
.clone();
134+
let name = package.name.to_string();
135+
136+
// This versionm of wit_component requires a flag for v2 encoding.
137+
// v1 encoding is retired in wit_component main. You can remove the
138+
// flag when this breaks next time we upgrade the crate!
139+
let bytes = wit_component::encode(Some(true), decoded.resolve(), package_id)?;
140+
141+
Ok(Self {
142+
name,
143+
decoded,
144+
package,
145+
package_id,
146+
package_bytes: bytes,
147+
})
148+
}
149+
82150
/// Returns true if the given trigger type provides the world identified by
83151
/// `world` in this environment.
84152
pub fn is_world_for(&self, trigger_type: &TriggerType, world: &wit_parser::World) -> bool {

crates/environments/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use environment_definition::{load_environment, TargetEnvironment, TriggerType};
77
use futures::future::try_join_all;
88
pub use loader::ApplicationToValidate;
99
use loader::ComponentToValidate;
10+
use spin_manifest::schema::v2::TargetEnvironmentRef;
1011

1112
pub async fn validate_application_against_environment_ids(
1213
application: &ApplicationToValidate,
13-
env_ids: &[impl AsRef<str>],
14+
env_ids: &[TargetEnvironmentRef],
1415
) -> anyhow::Result<Vec<anyhow::Error>> {
1516
if env_ids.is_empty() {
1617
return Ok(Default::default());

crates/manifest/src/schema/v2.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub struct AppDetails {
5858
pub authors: Vec<String>,
5959
/// `targets = ["spin-2.5", "fermyon-cloud", "spinkube-0.4"]`
6060
#[serde(default, skip_serializing_if = "Vec::is_empty")]
61-
pub targets: Vec<String>,
61+
pub targets: Vec<TargetEnvironmentRef>,
6262
/// `[application.triggers.<type>]`
6363
#[serde(rename = "trigger", default, skip_serializing_if = "Map::is_empty")]
6464
pub trigger_global_configs: Map<String, toml::Table>,
@@ -340,6 +340,27 @@ impl ComponentDependencies {
340340
}
341341
}
342342

343+
/// Identifies a deployment target.
344+
#[derive(Clone, Debug, Serialize, Deserialize)]
345+
#[serde(untagged, deny_unknown_fields)]
346+
pub enum TargetEnvironmentRef {
347+
/// Environment package reference e.g. `spin:cli@3.0`. This is looked up
348+
/// in the default environment registry.
349+
DefaultRegistry(String),
350+
/// A target package in a registry other than the default
351+
Registry {
352+
/// Registry hosting the environment package e.g. `fermyon.com``.
353+
registry: String,
354+
/// Environment package reference e.g. `my:spin-env@1.2`.
355+
package: String,
356+
},
357+
/// A filesystem directory. This is expected to contain a WIT package.
358+
WitDirectory {
359+
/// The directory containing the environment WIT.
360+
path: PathBuf,
361+
},
362+
}
363+
343364
mod kebab_or_snake_case {
344365
use serde::{Deserialize, Serialize};
345366
pub use spin_serde::{KebabId, SnakeId};

0 commit comments

Comments
 (0)