upstream_rs/services/builder/profiles/
dotnet.rs1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use anyhow::{Context, Result, anyhow, bail};
5
6use crate::services::builder::{
7 BuildProfile,
8 profiles::{BuildProfileHandler, emit_line_callback, run_command_with_line_callback},
9};
10
11pub struct DotnetProfile;
12
13impl DotnetProfile {
14 fn binary_name(package_name: &str) -> String {
15 #[cfg(windows)]
16 {
17 format!("{package_name}.exe")
18 }
19 #[cfg(not(windows))]
20 {
21 package_name.to_string()
22 }
23 }
24
25 fn find_project_dir(workspace: &Path) -> Option<PathBuf> {
26 let has_root = std::fs::read_dir(workspace).ok().is_some_and(|entries| {
27 entries.flatten().any(|e| {
28 e.path()
29 .extension()
30 .is_some_and(|ext| ext == "sln" || ext == "csproj")
31 })
32 });
33 if has_root {
34 Some(workspace.to_path_buf())
35 } else {
36 None
37 }
38 }
39}
40
41impl BuildProfileHandler for DotnetProfile {
42 fn profile(&self) -> BuildProfile {
43 BuildProfile::Dotnet
44 }
45
46 fn detect(&self, workspace: &Path) -> bool {
47 Self::find_project_dir(workspace).is_some()
48 }
49
50 fn run_build(
51 &self,
52 workspace: &Path,
53 package_name: &str,
54 line_callback: &mut Option<&mut dyn FnMut(&str)>,
55 ) -> Result<PathBuf> {
56 let project_dir = Self::find_project_dir(workspace).ok_or_else(|| {
57 anyhow!(
58 "Could not find a .sln or .csproj in repository root '{}'.",
59 workspace.display()
60 )
61 })?;
62
63 let publish_dir = project_dir.join(".upstream-build").join("publish");
64 std::fs::create_dir_all(&publish_dir).context(format!(
65 "Failed to create dotnet publish directory '{}'",
66 publish_dir.display()
67 ))?;
68
69 emit_line_callback(line_callback, "Running dotnet publish ...");
70 let status = run_command_with_line_callback(
71 Command::new("dotnet")
72 .arg("publish")
73 .arg("-c")
74 .arg("Release")
75 .arg("-o")
76 .arg(&publish_dir)
77 .current_dir(&project_dir),
78 "Failed to run 'dotnet publish'. Is .NET SDK installed?",
79 line_callback,
80 )?;
81
82 if !status.success() {
83 bail!("Dotnet publish failed for '{}'", package_name);
84 }
85
86 let candidate = publish_dir.join(Self::binary_name(package_name));
87
88 if !candidate.exists() {
89 return Err(anyhow!(
90 "Dotnet publish succeeded but artifact was not found at '{}'",
91 candidate.display()
92 ));
93 }
94
95 Ok(candidate)
96 }
97}