@@ -3,7 +3,9 @@ use crate::dotnet::solution::Solution;
33use crate :: { Project , utils} ;
44use libcnb:: data:: launch:: { Process , ProcessBuilder , ProcessType } ;
55use libcnb:: data:: process_type;
6+ use std:: io;
67use std:: path:: { Path , PathBuf } ;
8+ use tracing:: instrument;
79
810/// Detects processes in a solution's projects
911pub ( crate ) fn detect_solution_processes ( app_dir : & Path , solution : & Solution ) -> Vec < Process > {
@@ -18,8 +20,16 @@ pub(crate) fn detect_solution_processes(app_dir: &Path, solution: &Solution) ->
1820 solution
1921 . projects
2022 . iter ( )
23+ . filter ( |project| {
24+ matches ! (
25+ project. project_type,
26+ ProjectType :: ConsoleApplication
27+ | ProjectType :: WebApplication
28+ | ProjectType :: WorkerService
29+ )
30+ } )
2131 . filter_map ( |project| {
22- let mut process = project_launch_process ( app_dir, project) ?;
32+ let mut process = project_launch_process ( app_dir, project) . ok ( ) ?;
2333
2434 // If it's a web app and the only one, override its type and make it default.
2535 if has_single_web_app && project. project_type == ProjectType :: WebApplication {
@@ -32,21 +42,27 @@ pub(crate) fn detect_solution_processes(app_dir: &Path, solution: &Solution) ->
3242 . collect ( )
3343}
3444
35- /// Determines if a project should have a launchable process and constructs it
36- fn project_launch_process ( app_dir : & Path , project : & Project ) -> Option < Process > {
37- if !matches ! (
38- project. project_type,
39- ProjectType :: ConsoleApplication | ProjectType :: WebApplication | ProjectType :: WorkerService
40- ) {
41- return None ;
45+ #[ instrument( skip( app_dir) , err) ]
46+ fn project_launch_process ( app_dir : & Path , project : & Project ) -> io:: Result < Process > {
47+ let executable_path = project_executable_path ( project) ;
48+
49+ if !executable_path. exists ( ) {
50+ return Err ( io:: Error :: new (
51+ io:: ErrorKind :: NotFound ,
52+ format ! ( "Executable not found: {}" , executable_path. display( ) ) ,
53+ ) ) ;
4254 }
43- let relative_executable_path = relative_executable_path ( app_dir, project) ;
55+
56+ let relative_executable_path = executable_path
57+ . strip_prefix ( app_dir)
58+ . expect ( "Executable path should be inside the app directory" )
59+ . to_path_buf ( ) ;
4460
4561 let command = build_command ( & relative_executable_path, project. project_type ) ;
4662
4763 let process_type = project_process_type ( project) ;
4864
49- Some ( ProcessBuilder :: new ( process_type, [ "bash" , "-c" , & command] ) . build ( ) )
65+ Ok ( ProcessBuilder :: new ( process_type, [ "bash" , "-c" , & command] ) . build ( ) )
5066}
5167
5268/// Constructs the shell command for launching the process
@@ -84,14 +100,6 @@ fn project_process_type(project: &Project) -> ProcessType {
84100 . expect ( "Sanitized process type name should always be valid" )
85101}
86102
87- /// Returns the (expected) relative executable path from the app directory
88- fn relative_executable_path ( app_dir : & Path , project : & Project ) -> PathBuf {
89- project_executable_path ( project)
90- . strip_prefix ( app_dir)
91- . expect ( "Executable path should be inside the app directory" )
92- . to_path_buf ( )
93- }
94-
95103/// Returns the (expected) absolute path to the project's compiled executable
96104fn project_executable_path ( project : & Project ) -> PathBuf {
97105 project
@@ -108,6 +116,7 @@ mod tests {
108116 use super :: * ;
109117 use libcnb:: data:: launch:: { Process , WorkingDirectory } ;
110118 use libcnb:: data:: process_type;
119+ use std:: fs;
111120 use std:: path:: PathBuf ;
112121
113122 fn create_test_project ( path : & str , assembly_name : & str , project_type : ProjectType ) -> Project {
@@ -119,18 +128,45 @@ mod tests {
119128 }
120129 }
121130
131+ fn create_executable_for_project ( project : & Project ) {
132+ let executable_path = project_executable_path ( project) ;
133+ fs:: create_dir_all ( executable_path. parent ( ) . unwrap ( ) ) . unwrap ( ) ;
134+ fs:: write ( & executable_path, "" ) . unwrap ( ) ;
135+ }
136+
122137 #[ test]
123- fn test_detect_solution_processes_single_web_app ( ) {
124- let app_dir = Path :: new ( "/tmp" ) ;
138+ fn test_detect_solution_processes_missing_project_executable ( ) {
139+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
140+ let app_dir = temp_dir. path ( ) ;
141+
125142 let solution = Solution {
126- path : PathBuf :: from ( "/tmp/ foo.sln") ,
143+ path : app_dir . join ( " foo.sln") ,
127144 projects : vec ! [ create_test_project(
128- "/tmp/ bar/bar.csproj",
145+ & format! ( "{}/ bar/bar.csproj", app_dir . display ( ) ) ,
129146 "bar" ,
130147 ProjectType :: WebApplication ,
131148 ) ] ,
132149 } ;
133150
151+ assert ! ( detect_solution_processes( app_dir, & solution) . is_empty( ) ) ;
152+ }
153+
154+ #[ test]
155+ fn test_detect_solution_processes_single_web_app ( ) {
156+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
157+ let app_dir = temp_dir. path ( ) ;
158+ let project = create_test_project (
159+ & format ! ( "{}/bar/bar.csproj" , app_dir. display( ) ) ,
160+ "bar" ,
161+ ProjectType :: WebApplication ,
162+ ) ;
163+ create_executable_for_project ( & project) ;
164+
165+ let solution = Solution {
166+ path : app_dir. join ( "foo.sln" ) ,
167+ projects : vec ! [ project] ,
168+ } ;
169+
134170 let expected_processes = vec ! [ Process {
135171 r#type: process_type!( "web" ) ,
136172 command: vec![
@@ -151,13 +187,24 @@ mod tests {
151187
152188 #[ test]
153189 fn test_detect_solution_processes_multiple_web_apps ( ) {
154- let app_dir = Path :: new ( "/tmp" ) ;
190+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
191+ let app_dir = temp_dir. path ( ) ;
192+ let project1 = create_test_project (
193+ & format ! ( "{}/bar/bar.csproj" , app_dir. display( ) ) ,
194+ "bar" ,
195+ ProjectType :: WebApplication ,
196+ ) ;
197+ let project2 = create_test_project (
198+ & format ! ( "{}/baz/baz.csproj" , app_dir. display( ) ) ,
199+ "baz" ,
200+ ProjectType :: WebApplication ,
201+ ) ;
202+ create_executable_for_project ( & project1) ;
203+ create_executable_for_project ( & project2) ;
204+
155205 let solution = Solution {
156- path : PathBuf :: from ( "/tmp/foo.sln" ) ,
157- projects : vec ! [
158- create_test_project( "/tmp/bar/bar.csproj" , "bar" , ProjectType :: WebApplication ) ,
159- create_test_project( "/tmp/baz/baz.csproj" , "baz" , ProjectType :: WebApplication ) ,
160- ] ,
206+ path : app_dir. join ( "foo.sln" ) ,
207+ projects : vec ! [ project1, project2] ,
161208 } ;
162209 assert_eq ! (
163210 detect_solution_processes( app_dir, & solution)
@@ -170,18 +217,29 @@ mod tests {
170217
171218 #[ test]
172219 fn test_detect_solution_processes_single_web_app_and_console_app ( ) {
173- let app_dir = Path :: new ( "/tmp" ) ;
220+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
221+ let app_dir = temp_dir. path ( ) ;
222+ let project1 = create_test_project (
223+ & format ! ( "{}/qux/qux.csproj" , app_dir. display( ) ) ,
224+ "qux" ,
225+ ProjectType :: Unknown ,
226+ ) ;
227+ let project2 = create_test_project (
228+ & format ! ( "{}/bar/bar.csproj" , app_dir. display( ) ) ,
229+ "bar" ,
230+ ProjectType :: WebApplication ,
231+ ) ;
232+ let project3 = create_test_project (
233+ & format ! ( "{}/baz/baz.csproj" , app_dir. display( ) ) ,
234+ "baz" ,
235+ ProjectType :: ConsoleApplication ,
236+ ) ;
237+ create_executable_for_project ( & project2) ;
238+ create_executable_for_project ( & project3) ;
239+
174240 let solution = Solution {
175- path : PathBuf :: from ( "/tmp/foo.sln" ) ,
176- projects : vec ! [
177- create_test_project( "/tmp/qux/qux.csproj" , "qux" , ProjectType :: Unknown ) ,
178- create_test_project( "/tmp/bar/bar.csproj" , "bar" , ProjectType :: WebApplication ) ,
179- create_test_project(
180- "/tmp/baz/baz.csproj" ,
181- "baz" ,
182- ProjectType :: ConsoleApplication ,
183- ) ,
184- ] ,
241+ path : app_dir. join ( "foo.sln" ) ,
242+ projects : vec ! [ project1, project2, project3] ,
185243 } ;
186244 assert_eq ! (
187245 detect_solution_processes( app_dir, & solution)
@@ -194,14 +252,21 @@ mod tests {
194252
195253 #[ test]
196254 fn test_detect_solution_processes_with_spaces ( ) {
197- let app_dir = Path :: new ( "/tmp" ) ;
255+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
256+ let app_dir = temp_dir. path ( ) ;
257+ let project = create_test_project (
258+ & format ! (
259+ "{}/My Project With Spaces/project.csproj" ,
260+ app_dir. display( )
261+ ) ,
262+ "My App" ,
263+ ProjectType :: ConsoleApplication ,
264+ ) ;
265+ create_executable_for_project ( & project) ;
266+
198267 let solution = Solution {
199- path : PathBuf :: from ( "/tmp/My Solution With Spaces.sln" ) ,
200- projects : vec ! [ create_test_project(
201- "/tmp/My Project With Spaces/project.csproj" ,
202- "My App" ,
203- ProjectType :: ConsoleApplication ,
204- ) ] ,
268+ path : app_dir. join ( "My Solution With Spaces.sln" ) ,
269+ projects : vec ! [ project] ,
205270 } ;
206271
207272 let expected_processes = vec ! [ Process {
@@ -222,21 +287,6 @@ mod tests {
222287 ) ;
223288 }
224289
225- #[ test]
226- fn test_relative_executable_path ( ) {
227- let app_dir = Path :: new ( "/tmp" ) ;
228- let project = create_test_project (
229- "/tmp/project/project.csproj" ,
230- "TestApp" ,
231- ProjectType :: ConsoleApplication ,
232- ) ;
233-
234- assert_eq ! (
235- relative_executable_path( app_dir, & project) ,
236- PathBuf :: from( "project/bin/publish/TestApp" )
237- ) ;
238- }
239-
240290 #[ test]
241291 fn test_project_executable_path ( ) {
242292 let project = create_test_project (
@@ -279,14 +329,18 @@ mod tests {
279329
280330 #[ test]
281331 fn test_detect_solution_processes_nested_solution ( ) {
282- let app_dir = Path :: new ( "/tmp" ) ;
332+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
333+ let app_dir = temp_dir. path ( ) ;
334+ let project = create_test_project (
335+ & format ! ( "{}/src/MyApp/MyApp.csproj" , app_dir. display( ) ) , // Project is also in src/ subdirectory
336+ "MyApp" ,
337+ ProjectType :: WebApplication ,
338+ ) ;
339+ create_executable_for_project ( & project) ;
340+
283341 let solution = Solution {
284- path : PathBuf :: from ( "/tmp/src/MyApp.sln" ) , // Solution is in src/ subdirectory
285- projects : vec ! [ create_test_project(
286- "/tmp/src/MyApp/MyApp.csproj" , // Project is also in src/ subdirectory
287- "MyApp" ,
288- ProjectType :: WebApplication ,
289- ) ] ,
342+ path : app_dir. join ( "src/MyApp.sln" ) , // Solution is in src/ subdirectory
343+ projects : vec ! [ project] ,
290344 } ;
291345
292346 let expected_processes = vec ! [ Process {
0 commit comments