@@ -16,31 +16,87 @@ use subprocess::{Exec, Redirection};
16
16
use crate :: manifest:: component_build_configs;
17
17
18
18
/// 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
+ target_checks : TargetChecking ,
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
+ } ) ?;
29
33
let app_dir = parent_dir ( manifest_file) ?;
30
34
31
- let build_result = build_components ( component_ids, components, app_dir) ;
35
+ let build_result = build_components ( component_ids, build_info . components ( ) , & app_dir) ;
32
36
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.
34
41
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
+ target_checks. check ( ) && 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 target_checks. check ( ) {
63
+ let application = spin_environments:: ApplicationToValidate :: new (
64
+ manifest. clone ( ) ,
65
+ manifest_file. parent ( ) . unwrap ( ) ,
66
+ )
67
+ . await
68
+ . context ( "unable to load application for checking against deployment targets" ) ?;
69
+ let target_validation = spin_environments:: validate_application_against_environment_ids (
70
+ & application,
71
+ build_info. deployment_targets ( ) ,
72
+ cache_root. clone ( ) ,
73
+ & app_dir,
74
+ )
75
+ . await
76
+ . context ( "unable to check if the application is compatible with deployment targets" ) ?;
77
+
78
+ if !target_validation. is_ok ( ) {
79
+ for error in target_validation. errors ( ) {
80
+ terminal:: error!( "{error}" ) ;
81
+ }
82
+ anyhow:: bail!( "All components built successfully, but one or more was incompatible with one or more of the deployment targets." ) ;
83
+ }
35
84
}
36
85
37
- build_result
86
+ Ok ( ( ) )
87
+ }
88
+
89
+ /// Run all component build commands, using the default options (build all
90
+ /// components, perform target checking). We run a "default build" in several
91
+ /// places and this centralises the logic of what such a "default build" means.
92
+ pub async fn build_default ( manifest_file : & Path , cache_root : Option < PathBuf > ) -> Result < ( ) > {
93
+ build ( manifest_file, & [ ] , TargetChecking :: Check , cache_root) . await
38
94
}
39
95
40
96
fn build_components (
41
97
component_ids : & [ String ] ,
42
98
components : Vec < ComponentBuildInfo > ,
43
- app_dir : PathBuf ,
99
+ app_dir : & Path ,
44
100
) -> Result < ( ) , anyhow:: Error > {
45
101
let components_to_build = if component_ids. is_empty ( ) {
46
102
components
@@ -70,7 +126,7 @@ fn build_components(
70
126
71
127
components_to_build
72
128
. into_iter ( )
73
- . map ( |c| build_component ( c, & app_dir) )
129
+ . map ( |c| build_component ( c, app_dir) )
74
130
. collect :: < Result < Vec < _ > , _ > > ( ) ?;
75
131
76
132
terminal:: step!( "Finished" , "building all Spin components" ) ;
@@ -159,6 +215,21 @@ fn construct_workdir(app_dir: &Path, workdir: Option<impl AsRef<Path>>) -> Resul
159
215
Ok ( cwd)
160
216
}
161
217
218
+ /// Specifies target environment checking behaviour
219
+ pub enum TargetChecking {
220
+ /// The build should check that all components are compatible with all target environments.
221
+ Check ,
222
+ /// The build should not check target environments.
223
+ Skip ,
224
+ }
225
+
226
+ impl TargetChecking {
227
+ /// Should the build check target environments?
228
+ fn check ( & self ) -> bool {
229
+ matches ! ( self , Self :: Check )
230
+ }
231
+ }
232
+
162
233
#[ cfg( test) ]
163
234
mod tests {
164
235
use super :: * ;
@@ -171,6 +242,8 @@ mod tests {
171
242
#[ tokio:: test]
172
243
async fn can_load_even_if_trigger_invalid ( ) {
173
244
let bad_trigger_file = test_data_root ( ) . join ( "bad_trigger.toml" ) ;
174
- build ( & bad_trigger_file, & [ ] ) . await . unwrap ( ) ;
245
+ build ( & bad_trigger_file, & [ ] , TargetChecking :: Skip , None )
246
+ . await
247
+ . unwrap ( ) ;
175
248
}
176
249
}
0 commit comments