Skip to content

Commit 651aca5

Browse files
committed
Validate against target environments during build
Signed-off-by: itowlson <[email protected]>
1 parent 4223521 commit 651aca5

File tree

19 files changed

+1492
-238
lines changed

19 files changed

+1492
-238
lines changed

Cargo.lock

+140-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ spin-app = { path = "crates/app" }
5555
spin-build = { path = "crates/build" }
5656
spin-common = { path = "crates/common" }
5757
spin-doctor = { path = "crates/doctor" }
58+
spin-environments = { path = "crates/environments" }
5859
spin-factor-outbound-networking = { path = "crates/factor-outbound-networking" }
5960
spin-http = { path = "crates/http" }
6061
spin-loader = { path = "crates/loader" }

crates/build/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = { workspace = true }
88
anyhow = { workspace = true }
99
serde = { workspace = true }
1010
spin-common = { path = "../common" }
11+
spin-environments = { path = "../environments" }
1112
spin-manifest = { path = "../manifest" }
1213
subprocess = "0.2"
1314
terminal = { path = "../terminal" }

crates/build/src/lib.rs

+64-16
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,79 @@ use subprocess::{Exec, Redirection};
1616
use crate::manifest::component_build_configs;
1717

1818
/// If present, run the build command of each component.
19-
pub async fn build(manifest_file: &Path, component_ids: &[String]) -> Result<()> {
20-
let (components, manifest_err) =
21-
component_build_configs(manifest_file)
22-
.await
23-
.with_context(|| {
24-
format!(
25-
"Cannot read manifest file from {}",
26-
quoted_path(manifest_file)
27-
)
28-
})?;
19+
pub async fn build(
20+
manifest_file: &Path,
21+
component_ids: &[String],
22+
skip_target_checks: bool,
23+
cache_root: Option<PathBuf>,
24+
) -> Result<()> {
25+
let build_info = component_build_configs(manifest_file)
26+
.await
27+
.with_context(|| {
28+
format!(
29+
"Cannot read manifest file from {}",
30+
quoted_path(manifest_file)
31+
)
32+
})?;
2933
let app_dir = parent_dir(manifest_file)?;
3034

31-
let build_result = build_components(component_ids, components, app_dir);
35+
let build_result = build_components(component_ids, build_info.components(), &app_dir);
3236

33-
if let Some(e) = manifest_err {
37+
// Emit any required warnings now, so that they don't bury any errors.
38+
if let Some(e) = build_info.load_error() {
39+
// The manifest had errors. We managed to attempt a build anyway, but we want to
40+
// let the user know about them.
3441
terminal::warn!("The manifest has errors not related to the Wasm component build. Error details:\n{e:#}");
42+
// Checking deployment targets requires a healthy manifest (because trigger types etc.),
43+
// if any of these were specified, warn they are being skipped.
44+
let should_have_checked_targets =
45+
!skip_target_checks && build_info.has_deployment_targets();
46+
if should_have_checked_targets {
47+
terminal::warn!(
48+
"The manifest error(s) prevented Spin from checking the deployment targets."
49+
);
50+
}
51+
}
52+
53+
// If the build failed, exit with an error at this point.
54+
build_result?;
55+
56+
let Some(manifest) = build_info.manifest() else {
57+
// We can't proceed to checking (because that needs a full healthy manifest), and we've
58+
// already emitted any necessary warning, so quit.
59+
return Ok(());
60+
};
61+
62+
if !skip_target_checks {
63+
let application = spin_environments::ApplicationToValidate::new(
64+
manifest.clone(),
65+
manifest_file.parent().unwrap(),
66+
)
67+
.await?;
68+
let errors = spin_environments::validate_application_against_environment_ids(
69+
&application,
70+
build_info.deployment_targets(),
71+
cache_root.clone(),
72+
&app_dir,
73+
)
74+
.await?;
75+
76+
for error in &errors {
77+
terminal::error!("{error}");
78+
}
79+
80+
if !errors.is_empty() {
81+
anyhow::bail!("All components built successfully, but one or more was incompatible with one or more of the deployment targets.");
82+
}
3583
}
3684

37-
build_result
85+
Ok(())
3886
}
3987

4088
fn build_components(
4189
component_ids: &[String],
4290
components: Vec<ComponentBuildInfo>,
43-
app_dir: PathBuf,
91+
app_dir: &Path,
4492
) -> Result<(), anyhow::Error> {
4593
let components_to_build = if component_ids.is_empty() {
4694
components
@@ -70,7 +118,7 @@ fn build_components(
70118

71119
components_to_build
72120
.into_iter()
73-
.map(|c| build_component(c, &app_dir))
121+
.map(|c| build_component(c, app_dir))
74122
.collect::<Result<Vec<_>, _>>()?;
75123

76124
terminal::step!("Finished", "building all Spin components");
@@ -171,6 +219,6 @@ mod tests {
171219
#[tokio::test]
172220
async fn can_load_even_if_trigger_invalid() {
173221
let bad_trigger_file = test_data_root().join("bad_trigger.toml");
174-
build(&bad_trigger_file, &[]).await.unwrap();
222+
build(&bad_trigger_file, &[], true, None).await.unwrap();
175223
}
176224
}

0 commit comments

Comments
 (0)