Skip to content

Commit ef342c8

Browse files
committed
Report all target validation fails not just the first
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent 2d617b6 commit ef342c8

File tree

6 files changed

+60
-66
lines changed

6 files changed

+60
-66
lines changed

crates/build/src/deployment.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

crates/build/src/lib.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
//! A library for building Spin components.
44
5-
mod deployment;
65
mod manifest;
76

87
use anyhow::{anyhow, bail, Context, Result};
@@ -17,7 +16,11 @@ use subprocess::{Exec, Redirection};
1716
use crate::manifest::component_build_configs;
1817

1918
/// If present, run the build command of each component.
20-
pub async fn build(manifest_file: &Path, component_ids: &[String], skip_target_checks: bool) -> Result<()> {
19+
pub async fn build(
20+
manifest_file: &Path,
21+
component_ids: &[String],
22+
skip_target_checks: bool,
23+
) -> Result<()> {
2124
let (components, deployment_targets, manifest) = component_build_configs(manifest_file)
2225
.await
2326
.with_context(|| {
@@ -37,16 +40,23 @@ pub async fn build(manifest_file: &Path, component_ids: &[String], skip_target_c
3740
build_result?;
3841

3942
if let Ok(manifest) = &manifest {
40-
let should_check_targets = !skip_target_checks && !deployment_targets.is_empty();
41-
if should_check_targets {
43+
if !skip_target_checks {
4244
let resolution_context =
4345
spin_environments::ResolutionContext::new(manifest_file.parent().unwrap()).await?;
44-
spin_environments::validate_application_against_environment_ids(
45-
deployment_targets.iter(),
46+
let errors = spin_environments::validate_application_against_environment_ids(
47+
&deployment_targets,
4648
manifest,
4749
&resolution_context,
4850
)
4951
.await?;
52+
53+
for error in &errors {
54+
terminal::error!("{error}");
55+
}
56+
57+
if !errors.is_empty() {
58+
anyhow::bail!("All components built successfully, but one or more was incompatible with one or more of the deployment targets.");
59+
}
5060
}
5161
}
5262

crates/build/src/manifest.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use std::{collections::BTreeMap, path::Path};
44

55
use spin_manifest::{schema::v2, ManifestVersion};
66

7-
use crate::deployment::DeploymentTargets;
7+
pub type DeploymentTarget = String;
8+
pub type DeploymentTargets = Vec<DeploymentTarget>;
89

910
/// Returns a map of component IDs to [`v2::ComponentBuildConfig`]s for the
1011
/// given (v1 or v2) manifest path. If the manifest cannot be loaded, the
@@ -49,13 +50,7 @@ fn build_configs_from_manifest(
4950
fn deployment_targets_from_manifest(
5051
manifest: &spin_manifest::schema::v2::AppManifest,
5152
) -> DeploymentTargets {
52-
let target_environments = manifest.application.targets.clone();
53-
// let components = manifest
54-
// .components
55-
// .iter()
56-
// .map(|(id, c)| (id.to_string(), c.source.clone()))
57-
// .collect();
58-
DeploymentTargets::new(target_environments)
53+
manifest.application.targets.clone()
5954
}
6055

6156
async fn fallback_load_build_configs(
@@ -83,13 +78,6 @@ async fn fallback_load_build_configs(
8378
async fn fallback_load_deployment_targets(
8479
manifest_file: impl AsRef<Path>,
8580
) -> Result<DeploymentTargets> {
86-
// fn try_parse_component_source(c: (&String, &toml::Value)) -> Option<(String, spin_manifest::schema::v2::ComponentSource)> {
87-
// let (id, ctab) = c;
88-
// let cs = ctab.as_table()
89-
// .and_then(|c| c.get("source"))
90-
// .and_then(|cs| spin_manifest::schema::v2::ComponentSource::deserialize(cs.clone()).ok());
91-
// cs.map(|cs| (id.to_string(), cs))
92-
// }
9381
let manifest_text = tokio::fs::read_to_string(manifest_file).await?;
9482
Ok(match ManifestVersion::detect(&manifest_text)? {
9583
ManifestVersion::V1 => Default::default(),
@@ -106,12 +94,7 @@ async fn fallback_load_deployment_targets(
10694
.filter_map(|t| t.as_str())
10795
.map(|s| s.to_owned())
10896
.collect();
109-
// let components = table
110-
// .get("component")
111-
// .and_then(|cs| cs.as_table())
112-
// .map(|table| table.iter().filter_map(try_parse_component_source).collect())
113-
// .unwrap_or_default();
114-
DeploymentTargets::new(target_environments)
97+
target_environments
11598
}
11699
})
117100
}

crates/environments/src/environment_definition.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use anyhow::Context;
22

3-
pub async fn load_environment(env_id: &str) -> anyhow::Result<TargetEnvironment> {
3+
pub async fn load_environment(env_id: impl AsRef<str>) -> anyhow::Result<TargetEnvironment> {
44
use futures_util::TryStreamExt;
55

6+
let env_id = env_id.as_ref();
7+
68
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?"))?;
79

810
// TODO: this requires wkg configuration which shouldn't be on users:

crates/environments/src/lib.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,23 @@ pub use loader::ResolutionContext;
88
use loader::{load_and_resolve_all, ComponentToValidate};
99

1010
pub async fn validate_application_against_environment_ids(
11-
env_ids: impl Iterator<Item = &str>,
11+
env_ids: &[impl AsRef<str>],
1212
app: &spin_manifest::schema::v2::AppManifest,
1313
resolution_context: &ResolutionContext,
14-
) -> anyhow::Result<()> {
15-
let envs = join_all_result(env_ids.map(load_environment)).await?;
14+
) -> anyhow::Result<Vec<anyhow::Error>> {
15+
if env_ids.is_empty() {
16+
return Ok(Default::default());
17+
}
18+
19+
let envs = join_all_result(env_ids.iter().map(load_environment)).await?;
1620
validate_application_against_environments(&envs, app, resolution_context).await
1721
}
1822

1923
async fn validate_application_against_environments(
2024
envs: &[TargetEnvironment],
2125
app: &spin_manifest::schema::v2::AppManifest,
2226
resolution_context: &ResolutionContext,
23-
) -> anyhow::Result<()> {
27+
) -> anyhow::Result<Vec<anyhow::Error>> {
2428
use futures::FutureExt;
2529

2630
for trigger_type in app.triggers.keys() {
@@ -40,31 +44,45 @@ async fn validate_application_against_environments(
4044
.await
4145
.context("Failed to prepare components for target environment checking")?;
4246

47+
let mut errs = vec![];
48+
4349
for (trigger_type, component) in components_by_trigger_type {
4450
for component in &component {
45-
validate_component_against_environments(envs, &trigger_type, component).await?;
51+
errs.extend(
52+
validate_component_against_environments(envs, &trigger_type, component).await?,
53+
);
4654
}
4755
}
4856

49-
Ok(())
57+
Ok(errs)
5058
}
5159

5260
async fn validate_component_against_environments(
5361
envs: &[TargetEnvironment],
5462
trigger_type: &TriggerType,
5563
component: &ComponentToValidate<'_>,
56-
) -> anyhow::Result<()> {
64+
) -> anyhow::Result<Vec<anyhow::Error>> {
65+
let mut errs = vec![];
66+
5767
for env in envs {
5868
let worlds = env.worlds(trigger_type);
59-
validate_wasm_against_any_world(env, &worlds, component).await?;
69+
if let Some(e) = validate_wasm_against_any_world(env, &worlds, component)
70+
.await
71+
.err()
72+
{
73+
errs.push(e);
74+
}
6075
}
6176

62-
tracing::info!(
63-
"Validated component {} {} against all target worlds",
64-
component.id(),
65-
component.source_description()
66-
);
67-
Ok(())
77+
if errs.is_empty() {
78+
tracing::info!(
79+
"Validated component {} {} against all target worlds",
80+
component.id(),
81+
component.source_description()
82+
);
83+
}
84+
85+
Ok(errs)
6886
}
6987

7088
async fn validate_wasm_against_any_world(

src/commands/build.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ pub struct BuildCommand {
3030
/// By default, if the application manifest specifies one or more deployment targets, Spin
3131
/// checks that all components are compatible with those deployment targets. Specify
3232
/// this option to bypass those target checks.
33-
#[clap(long = "skip-target-checks", alias = "skip-target-check", takes_value = false)]
33+
#[clap(
34+
long = "skip-target-checks",
35+
alias = "skip-target-check",
36+
takes_value = false
37+
)]
3438
skip_target_checks: bool,
3539

3640
/// Run the application after building.

0 commit comments

Comments
 (0)