@@ -13,13 +13,14 @@ use std::str::FromStr;
1313use crate :: agent:: tools:: error:: { ErrorCategory , format_error_for_llm} ;
1414use crate :: analyzer:: { AnalysisConfig , TechnologyCategory , analyze_project_with_config} ;
1515use crate :: platform:: api:: types:: {
16- CloudProvider , CreateDeploymentConfigRequest , build_cloud_runner_config,
16+ CloudProvider , CreateDeploymentConfigRequest , ProjectRepository , build_cloud_runner_config,
1717} ;
1818use crate :: platform:: api:: { PlatformApiClient , PlatformApiError , TriggerDeploymentRequest } ;
1919use crate :: platform:: PlatformSession ;
2020use crate :: wizard:: {
2121 RecommendationInput , recommend_deployment, get_provider_deployment_statuses,
2222} ;
23+ use std:: process:: Command ;
2324
2425/// Arguments for the deploy service tool
2526#[ derive( Debug , Deserialize ) ]
@@ -534,7 +535,8 @@ User: "deploy this service"
534535 }
535536 } ;
536537
537- let repo = match repositories. repositories . first ( ) {
538+ // Smart repository selection: match local git remote or find non-gitops repo
539+ let repo = match find_matching_repository ( & repositories. repositories , & self . project_path ) {
538540 Some ( r) => r,
539541 None => {
540542 return Ok ( format_error_for_llm (
@@ -549,6 +551,13 @@ User: "deploy this service"
549551 }
550552 } ;
551553
554+ tracing:: info!(
555+ "Deploy service: Using repository {} (id: {}), default_branch: {:?}" ,
556+ repo. repository_full_name,
557+ repo. repository_id,
558+ repo. default_branch
559+ ) ;
560+
552561 // Use resolved environment ID from earlier
553562 if resolved_env_id. is_empty ( ) {
554563 return Ok ( format_error_for_llm (
@@ -726,6 +735,83 @@ fn get_service_name(path: &PathBuf) -> String {
726735 . unwrap_or_else ( || "service" . to_string ( ) )
727736}
728737
738+ /// Detect the git remote URL from a directory
739+ fn detect_git_remote ( project_path : & PathBuf ) -> Option < String > {
740+ let output = Command :: new ( "git" )
741+ . args ( [ "remote" , "get-url" , "origin" ] )
742+ . current_dir ( project_path)
743+ . output ( )
744+ . ok ( ) ?;
745+
746+ if output. status . success ( ) {
747+ let url = String :: from_utf8 ( output. stdout ) . ok ( ) ?;
748+ Some ( url. trim ( ) . to_string ( ) )
749+ } else {
750+ None
751+ }
752+ }
753+
754+ /// Parse repository full name from git remote URL
755+ /// Handles both SSH (git@github.com:owner/repo.git) and HTTPS (https://github.com/owner/repo.git)
756+ fn parse_repo_from_url ( url : & str ) -> Option < String > {
757+ let url = url. trim ( ) ;
758+
759+ // SSH format: git@github.com:owner/repo.git
760+ if url. starts_with ( "git@" ) {
761+ let parts: Vec < & str > = url. split ( ':' ) . collect ( ) ;
762+ if parts. len ( ) == 2 {
763+ let path = parts[ 1 ] . trim_end_matches ( ".git" ) ;
764+ return Some ( path. to_string ( ) ) ;
765+ }
766+ }
767+
768+ // HTTPS format: https://github.com/owner/repo.git
769+ if url. starts_with ( "https://" ) || url. starts_with ( "http://" ) {
770+ if let Some ( path) = url. split ( '/' ) . skip ( 3 ) . collect :: < Vec < _ > > ( ) . join ( "/" ) . strip_suffix ( ".git" ) {
771+ return Some ( path. to_string ( ) ) ;
772+ }
773+ // Without .git suffix
774+ let path: String = url. split ( '/' ) . skip ( 3 ) . collect :: < Vec < _ > > ( ) . join ( "/" ) ;
775+ if !path. is_empty ( ) {
776+ return Some ( path) ;
777+ }
778+ }
779+
780+ None
781+ }
782+
783+ /// Find repository matching local git remote, or fall back to non-gitops repo
784+ fn find_matching_repository < ' a > (
785+ repositories : & ' a [ ProjectRepository ] ,
786+ project_path : & PathBuf ,
787+ ) -> Option < & ' a ProjectRepository > {
788+ // First, try to detect from local git remote
789+ if let Some ( detected_name) = detect_git_remote ( project_path) . and_then ( |url| parse_repo_from_url ( & url) ) {
790+ tracing:: debug!( "Detected local git remote: {}" , detected_name) ;
791+
792+ if let Some ( repo) = repositories. iter ( ) . find ( |r| {
793+ r. repository_full_name . eq_ignore_ascii_case ( & detected_name)
794+ } ) {
795+ tracing:: debug!( "Matched detected repo: {}" , repo. repository_full_name) ;
796+ return Some ( repo) ;
797+ }
798+ }
799+
800+ // Fall back: find first non-GitOps repository
801+ // GitOps repos are typically infrastructure/config repos, not application repos
802+ if let Some ( repo) = repositories. iter ( ) . find ( |r| {
803+ r. is_primary_git_ops != Some ( true ) &&
804+ !r. repository_full_name . to_lowercase ( ) . contains ( "infrastructure" ) &&
805+ !r. repository_full_name . to_lowercase ( ) . contains ( "gitops" )
806+ } ) {
807+ tracing:: debug!( "Using non-gitops repo: {}" , repo. repository_full_name) ;
808+ return Some ( repo) ;
809+ }
810+
811+ // Last resort: first repo
812+ repositories. first ( )
813+ }
814+
729815/// Format a PlatformApiError for LLM consumption
730816fn format_api_error ( tool_name : & str , error : PlatformApiError ) -> String {
731817 match error {
0 commit comments