@@ -36,7 +36,11 @@ pub fn parse_ado_remote(remote_url: &str) -> Result<AdoContext> {
3636 let url = remote_url. trim ( ) ;
3737
3838 // SSH format: git@ssh.dev.azure.com:v3/{org}/{project}/{repo}
39- if let Some ( rest) = url. strip_prefix ( "git@ssh.dev.azure.com:v3/" ) {
39+ // Also handles legacy: git@vs-ssh.visualstudio.com:v3/{org}/{project}/{repo}
40+ if let Some ( rest) = url
41+ . strip_prefix ( "git@ssh.dev.azure.com:v3/" )
42+ . or_else ( || url. strip_prefix ( "git@vs-ssh.visualstudio.com:v3/" ) )
43+ {
4044 let parts: Vec < & str > = rest. splitn ( 3 , '/' ) . collect ( ) ;
4145 if parts. len ( ) >= 3 {
4246 let repo_name = parts[ 2 ] . trim_end_matches ( ".git" ) ;
@@ -290,12 +294,17 @@ async fn match_definitions(
290294 let yaml_path_str = pipeline. yaml_path . to_string_lossy ( ) ;
291295 let yaml_path_normalized = yaml_path_str. replace ( '\\' , "/" ) ;
292296
293- // Strategy 1: Match by YAML filename in the definition
297+ // Strategy 1: Match by YAML filename in the definition.
298+ // ADO stores yamlFilename with a leading '/' (e.g., "/.azdo/pipelines/agent.yml"),
299+ // so we strip it before comparing to the locally-detected relative path.
294300 let path_match = definitions. iter ( ) . find ( |d| {
295301 d. process
296302 . as_ref ( )
297303 . and_then ( |p| p. yaml_filename . as_ref ( ) )
298- . is_some_and ( |f| f. replace ( '\\' , "/" ) == yaml_path_normalized)
304+ . is_some_and ( |f| {
305+ let f_normalized = f. trim_start_matches ( '/' ) . replace ( '\\' , "/" ) ;
306+ f_normalized == yaml_path_normalized
307+ } )
299308 } ) ;
300309
301310 if let Some ( def) = path_match {
@@ -566,7 +575,7 @@ pub async fn run(
566575 }
567576 println ! ( ) ;
568577
569- // Step 5 : Update GITHUB_TOKEN
578+ // Step 4 : Update GITHUB_TOKEN
570579 if dry_run {
571580 println ! ( "Dry run \u{2014} no changes applied." ) ;
572581 println ! (
@@ -650,6 +659,15 @@ mod tests {
650659 assert_eq ! ( ctx. repo_name, "myrepo" ) ;
651660 }
652661
662+ #[ test]
663+ fn test_parse_ado_remote_legacy_ssh ( ) {
664+ let url = "git@vs-ssh.visualstudio.com:v3/myorg/myproject/myrepo" ;
665+ let ctx = parse_ado_remote ( url) . unwrap ( ) ;
666+ assert_eq ! ( ctx. org_url, "https://dev.azure.com/myorg" ) ;
667+ assert_eq ! ( ctx. project, "myproject" ) ;
668+ assert_eq ! ( ctx. repo_name, "myrepo" ) ;
669+ }
670+
653671 #[ test]
654672 fn test_parse_ado_remote_invalid ( ) {
655673 assert ! ( parse_ado_remote( "https://github.com/user/repo" ) . is_err( ) ) ;
@@ -666,6 +684,52 @@ mod tests {
666684 }
667685 }
668686
687+ fn make_def_with_yaml ( id : u64 , name : & str , yaml_filename : & str ) -> DefinitionSummary {
688+ DefinitionSummary {
689+ id,
690+ name : name. to_string ( ) ,
691+ process : Some ( ProcessInfo {
692+ yaml_filename : Some ( yaml_filename. to_string ( ) ) ,
693+ } ) ,
694+ }
695+ }
696+
697+ // ==================== YAML path matching ====================
698+
699+ #[ test]
700+ fn test_yaml_path_match_strips_leading_slash ( ) {
701+ // ADO stores yamlFilename with a leading '/'
702+ let def = make_def_with_yaml ( 1 , "My Pipeline" , "/.azdo/pipelines/agent.yml" ) ;
703+ let local_path = ".azdo/pipelines/agent.yml" ;
704+ let f_normalized = def
705+ . process
706+ . as_ref ( )
707+ . unwrap ( )
708+ . yaml_filename
709+ . as_ref ( )
710+ . unwrap ( )
711+ . trim_start_matches ( '/' )
712+ . replace ( '\\' , "/" ) ;
713+ assert_eq ! ( f_normalized, local_path) ;
714+ }
715+
716+ #[ test]
717+ fn test_yaml_path_match_without_leading_slash ( ) {
718+ // Some ADO instances may store without leading '/'
719+ let def = make_def_with_yaml ( 1 , "My Pipeline" , ".azdo/pipelines/agent.yml" ) ;
720+ let local_path = ".azdo/pipelines/agent.yml" ;
721+ let f_normalized = def
722+ . process
723+ . as_ref ( )
724+ . unwrap ( )
725+ . yaml_filename
726+ . as_ref ( )
727+ . unwrap ( )
728+ . trim_start_matches ( '/' )
729+ . replace ( '\\' , "/" ) ;
730+ assert_eq ! ( f_normalized, local_path) ;
731+ }
732+
669733 #[ test]
670734 fn test_fuzzy_match_single_unambiguous ( ) {
671735 let defs = vec ! [
0 commit comments