@@ -15,14 +15,18 @@ use subprocess::{Exec, Redirection};
15
15
16
16
use crate :: manifest:: component_build_configs;
17
17
18
+ const LAST_BUILD_PROFILE_FILE : & str = "last-build.txt" ;
19
+ const LAST_BUILD_ANON_VALUE : & str = "<anonymous>" ;
20
+
18
21
/// If present, run the build command of each component.
19
22
pub async fn build (
20
23
manifest_file : & Path ,
24
+ profile : Option < & str > ,
21
25
component_ids : & [ String ] ,
22
26
target_checks : TargetChecking ,
23
27
cache_root : Option < PathBuf > ,
24
28
) -> Result < ( ) > {
25
- let build_info = component_build_configs ( manifest_file)
29
+ let build_info = component_build_configs ( manifest_file, profile )
26
30
. await
27
31
. with_context ( || {
28
32
format ! (
@@ -53,6 +57,8 @@ pub async fn build(
53
57
// If the build failed, exit with an error at this point.
54
58
build_result?;
55
59
60
+ save_last_build_profile ( & app_dir, profile) ;
61
+
56
62
let Some ( manifest) = build_info. manifest ( ) else {
57
63
// We can't proceed to checking (because that needs a full healthy manifest), and we've
58
64
// already emitted any necessary warning, so quit.
@@ -71,6 +77,7 @@ pub async fn build(
71
77
build_info. deployment_targets ( ) ,
72
78
cache_root. clone ( ) ,
73
79
& app_dir,
80
+ profile,
74
81
)
75
82
. await
76
83
. context ( "unable to check if the application is compatible with deployment targets" ) ?;
@@ -89,8 +96,19 @@ pub async fn build(
89
96
/// Run all component build commands, using the default options (build all
90
97
/// components, perform target checking). We run a "default build" in several
91
98
/// 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
99
+ pub async fn build_default (
100
+ manifest_file : & Path ,
101
+ profile : Option < & str > ,
102
+ cache_root : Option < PathBuf > ,
103
+ ) -> Result < ( ) > {
104
+ build (
105
+ manifest_file,
106
+ profile,
107
+ & [ ] ,
108
+ TargetChecking :: Check ,
109
+ cache_root,
110
+ )
111
+ . await
94
112
}
95
113
96
114
fn build_components (
@@ -148,7 +166,7 @@ fn build_component(build_info: ComponentBuildInfo, app_dir: &Path) -> Result<()>
148
166
) ;
149
167
}
150
168
151
- for ( index, command) in b. commands ( ) . enumerate ( ) {
169
+ for ( index, command) in b. commands ( ) . iter ( ) . enumerate ( ) {
152
170
if command_count > 1 {
153
171
terminal:: step!(
154
172
"Running build step" ,
@@ -215,6 +233,56 @@ fn construct_workdir(app_dir: &Path, workdir: Option<impl AsRef<Path>>) -> Resul
215
233
Ok ( cwd)
216
234
}
217
235
236
+ /// Saves the build profile to the "last build profile" file.
237
+ /// Errors are ignored as they should not block building.
238
+ pub fn save_last_build_profile ( app_dir : & Path , profile : Option < & str > ) {
239
+ let app_stash_dir = app_dir. join ( ".spin" ) ;
240
+ _ = std:: fs:: create_dir_all ( & app_stash_dir) ;
241
+ let last_build_profile_file = app_stash_dir. join ( LAST_BUILD_PROFILE_FILE ) ;
242
+ _ = std:: fs:: write (
243
+ & last_build_profile_file,
244
+ profile. unwrap_or ( LAST_BUILD_ANON_VALUE ) ,
245
+ ) ;
246
+ }
247
+
248
+ /// Reads the last build profile from the "last build profile" file.
249
+ /// Errors are ignored.
250
+ pub fn read_last_build_profile ( app_dir : & Path ) -> Option < String > {
251
+ let app_stash_dir = app_dir. join ( ".spin" ) ;
252
+ let last_build_profile_file = app_stash_dir. join ( LAST_BUILD_PROFILE_FILE ) ;
253
+ let last_build_str = std:: fs:: read_to_string ( & last_build_profile_file) . ok ( ) ?;
254
+
255
+ if last_build_str == LAST_BUILD_ANON_VALUE {
256
+ None
257
+ } else {
258
+ Some ( last_build_str)
259
+ }
260
+ }
261
+
262
+ /// Prints a warning to stderr if the given profile is not the same
263
+ /// as the most recent build in the given application directory.
264
+ pub fn warn_if_not_latest_build ( manifest_path : & Path , profile : Option < & str > ) {
265
+ let Some ( app_dir) = manifest_path. parent ( ) else {
266
+ return ;
267
+ } ;
268
+
269
+ let latest_build = read_last_build_profile ( app_dir) ;
270
+
271
+ let is_match = match ( profile, latest_build) {
272
+ ( None , None ) => true ,
273
+ ( Some ( _) , None ) | ( None , Some ( _) ) => false ,
274
+ ( Some ( p) , Some ( latest) ) => p == latest,
275
+ } ;
276
+
277
+ if !is_match {
278
+ let profile_opt = match profile {
279
+ Some ( p) => format ! ( " --profile {p}" ) ,
280
+ None => "" . to_string ( ) ,
281
+ } ;
282
+ terminal:: warn!( "You built a different profile more recently than the one you are running. If the app appears to be behaving like an older version then run `spin up --build{profile_opt}`." ) ;
283
+ }
284
+ }
285
+
218
286
/// Specifies target environment checking behaviour
219
287
pub enum TargetChecking {
220
288
/// The build should check that all components are compatible with all target environments.
@@ -242,23 +310,23 @@ mod tests {
242
310
#[ tokio:: test]
243
311
async fn can_load_even_if_trigger_invalid ( ) {
244
312
let bad_trigger_file = test_data_root ( ) . join ( "bad_trigger.toml" ) ;
245
- build ( & bad_trigger_file, & [ ] , TargetChecking :: Skip , None )
313
+ build ( & bad_trigger_file, None , & [ ] , TargetChecking :: Skip , None )
246
314
. await
247
315
. unwrap ( ) ;
248
316
}
249
317
250
318
#[ tokio:: test]
251
319
async fn succeeds_if_target_env_matches ( ) {
252
320
let manifest_path = test_data_root ( ) . join ( "good_target_env.toml" ) ;
253
- build ( & manifest_path, & [ ] , TargetChecking :: Check , None )
321
+ build ( & manifest_path, None , & [ ] , TargetChecking :: Check , None )
254
322
. await
255
323
. unwrap ( ) ;
256
324
}
257
325
258
326
#[ tokio:: test]
259
327
async fn fails_if_target_env_does_not_match ( ) {
260
328
let manifest_path = test_data_root ( ) . join ( "bad_target_env.toml" ) ;
261
- let err = build ( & manifest_path, & [ ] , TargetChecking :: Check , None )
329
+ let err = build ( & manifest_path, None , & [ ] , TargetChecking :: Check , None )
262
330
. await
263
331
. expect_err ( "should have failed" )
264
332
. to_string ( ) ;
@@ -287,6 +355,7 @@ mod tests {
287
355
& manifest. application . targets ,
288
356
None ,
289
357
manifest_file. parent ( ) . unwrap ( ) ,
358
+ None ,
290
359
)
291
360
. await
292
361
. context ( "unable to check if the application is compatible with deployment targets" )
0 commit comments