Support building only specific components#1515
Conversation
crates/build/src/lib.rs
Outdated
| eprintln!("Component '{id}' does not have a build command."); | ||
| eprintln!("For information on specifying a build command, see https://developer.fermyon.com/spin/build#setting-up-for-spin-build."); |
There was a problem hiding this comment.
Should this return an error result? That would signal to doctor that the fix didn't work, and might be helpful for other kinds of automation.
There was a problem hiding this comment.
The doctor should never call this for components without a build command - it checks before recommending the "build" treatment.
Yeah I dithered about the error result. I think I kind of reached the conclusion that it hadn't failed, there was just nothing to do. And so I was a bit wary about Rust printing Error: ... which we can't currently stop it doing. But if other folks would prefer an error than I'm happy to go along with that.
There was a problem hiding this comment.
Looking at the existing flow, "no components have a build command" is currently not an error (and prints to stdout rather than stderr so I am being inconsistent there at least).
There was a problem hiding this comment.
I am reworking it as you suggested to take a list of components so this will unify it to the existing flow. I will use that existing flow for now; we can change it to be an error if we want.
|
Ah yes, I purposefully left this unimplemented to....drive developer engagement? |
| pub async fn run(self) -> Result<()> { | ||
| let manifest_file = crate::manifest::resolve_file_path(&self.app_source)?; | ||
| spin_build::build(&manifest_file).await?; | ||
| spin_build::build(&manifest_file, &self.component_id).await?; |
There was a problem hiding this comment.
Would it make sense for component_id to be a Vec<String> (allowing the flag to be specified multiple times)? I'm thinking that would give feature parity with the zombie PR #903
There was a problem hiding this comment.
It couldn't possibly do any harm, he said, cheerily placing his Jenga block.
|
Updated to allow multiple use, resulting in a slight change to some cases: |
| let unknown_component_ids: Vec<_> = component_ids | ||
| .iter() | ||
| .filter(|id| !all_ids.contains(id)) | ||
| .map(|s| s.as_str()) | ||
| .collect(); | ||
|
|
||
| if !unknown_component_ids.is_empty() { | ||
| bail!("Unknown component(s) {}", unknown_component_ids.join(", ")); | ||
| } |
There was a problem hiding this comment.
nit: Might as well avoid the unnecessary allocation in the happy path?
| let unknown_component_ids: Vec<_> = component_ids | |
| .iter() | |
| .filter(|id| !all_ids.contains(id)) | |
| .map(|s| s.as_str()) | |
| .collect(); | |
| if !unknown_component_ids.is_empty() { | |
| bail!("Unknown component(s) {}", unknown_component_ids.join(", ")); | |
| } | |
| let unknown_component_ids = component_ids | |
| .iter() | |
| .filter(|id| !all_ids.contains(id)) | |
| .peekable(); | |
| if unknown_component_ids.peek().is_some() { | |
| bail!("Unknown component(s) {}", unknown_component_ids.collect::<Vec<_>>().join(", ")); | |
| } |
There was a problem hiding this comment.
TIL: peekable() - thank you!
"Avoid allocating an empty vector" doesn't feel like a huge priority when the happy path goes on to run an external process (a compiler, no less). And I have to admit I find is_empty more obvious than peek().is_some(), though maybe the latter is idiomatic to Rust devs?
But I can change it (to this or @lann's suggestion) if other folks prefer. We could also use @lann's mention of itertools::join to simplify the sad path in this one.
There was a problem hiding this comment.
No need to block on my nits. I'll let you decide how to proceed from here. 😀
There was a problem hiding this comment.
Yeah same for my suggestions, which are more along the lines of "behold the majesty of the yak shave".
| let unknown_component_ids: Vec<_> = component_ids | ||
| .iter() | ||
| .filter(|id| !all_ids.contains(id)) | ||
| .map(|s| s.as_str()) | ||
| .collect(); | ||
|
|
||
| if !unknown_component_ids.is_empty() { | ||
| bail!("Unknown component(s) {}", unknown_component_ids.join(", ")); | ||
| } |
There was a problem hiding this comment.
Yet another option...
| let unknown_component_ids: Vec<_> = component_ids | |
| .iter() | |
| .filter(|id| !all_ids.contains(id)) | |
| .map(|s| s.as_str()) | |
| .collect(); | |
| if !unknown_component_ids.is_empty() { | |
| bail!("Unknown component(s) {}", unknown_component_ids.join(", ")); | |
| } | |
| let unknown_component_ids = component_ids | |
| .iter() | |
| .filter(|id| !all_ids.contains(id)) | |
| .map(|s| s.as_str()) | |
| .collect::<Vec<_>>() | |
| .join(", "); | |
| ensure!( | |
| unknown_component_ids.is_empty(), | |
| "Unknown component(s) {unknown_component_ids}", | |
| ); |
There was a problem hiding this comment.
Sub-option: itertools (which other spin crates already use) has itertools::join
There was a problem hiding this comment.
I thought about this but felt that "this set is empty" was more explanatory than "this string we are composing for UI purposes is empty."
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
spin doctoris set up to build only the component that the user asks it to fix, but thespin buildside of that was not ready in time. Now it is.Closes #903 [lann: should cover the use case]