@@ -9,6 +9,8 @@ const { loadAdapter, loadWorkflowConfig } = require("./adapter_config");
99const REPO_ROOT = path . resolve ( __dirname , ".." , ".." ) ;
1010const REMOTE = "origin" ;
1111const MAIN_REF = `${ REMOTE } /main` ;
12+ const SYNC_METADATA_MARKER = "api-md-review-sync" ;
13+ const SYNC_METADATA_WARNING = "DO NOT MODIFY THESE CONTENTS!" ;
1214let logger = console ;
1315
1416function logInfo ( message ) {
@@ -221,6 +223,14 @@ function packageRelDir(packageDir) {
221223 return path . relative ( REPO_ROOT , packageDir ) . split ( path . sep ) . join ( "/" ) ;
222224}
223225
226+ function normalizePackageDir ( packageDir ) {
227+ if ( path . isAbsolute ( packageDir ) ) {
228+ return packageRelDir ( packageDir ) ;
229+ }
230+
231+ return packageDir . split ( path . sep ) . join ( "/" ) ;
232+ }
233+
224234function apiMdPath ( packageDir ) {
225235 return path . join ( packageDir , "api.md" ) ;
226236}
@@ -593,6 +603,113 @@ function branchReferenceParts(headSelector) {
593603 } ;
594604}
595605
606+ function syncWorkingBranchInfo ( headSelector ) {
607+ if ( ! headSelector ) {
608+ return null ;
609+ }
610+
611+ const targetTag = resolveTargetTag ( headSelector ) ;
612+ if ( targetTag ) {
613+ return null ;
614+ }
615+
616+ const { owner, branch } = branchReferenceParts ( headSelector ) ;
617+ if ( owner === "Azure" && branch === "main" ) {
618+ return null ;
619+ }
620+
621+ return { owner, branch } ;
622+ }
623+
624+ function buildSyncMetadataObject ( { packageName, packageDir, baseBranch, reviewBranch, headSelector } ) {
625+ const workingBranch = syncWorkingBranchInfo ( headSelector ) ;
626+ if ( ! workingBranch ) {
627+ return null ;
628+ }
629+
630+ const metadata = {
631+ schemaVersion : 1 ,
632+ repository : "Azure/azure-sdk-for-python" ,
633+ packageName,
634+ packageDir : normalizePackageDir ( packageDir ) ,
635+ baseBranch,
636+ reviewBranch,
637+ workingOwner : workingBranch . owner ,
638+ workingBranch : workingBranch . branch ,
639+ } ;
640+
641+ const workingPr = findOpenPrForHead ( headSelector ) ;
642+ if ( workingPr && Number . isInteger ( workingPr . number ) ) {
643+ metadata . workingPrNumber = workingPr . number ;
644+ }
645+
646+ return metadata ;
647+ }
648+
649+ function buildSyncMetadataBlock ( metadata ) {
650+ if ( ! metadata ) {
651+ return null ;
652+ }
653+
654+ return [
655+ `<!-- ${ SYNC_METADATA_MARKER } ` ,
656+ SYNC_METADATA_WARNING ,
657+ JSON . stringify ( metadata , null , 2 ) ,
658+ "-->" ,
659+ ] . join ( "\n" ) ;
660+ }
661+
662+ function replaceSyncMetadataBlock ( body , metadataBlock ) {
663+ const cleanedBody = String ( body || "" )
664+ . replace ( new RegExp ( `<!--\\s*${ SYNC_METADATA_MARKER } [\\s\\S]*?-->\\s*` , "g" ) , "" )
665+ . trimEnd ( ) ;
666+
667+ if ( ! metadataBlock ) {
668+ return cleanedBody ;
669+ }
670+
671+ return `${ cleanedBody } \n\n${ metadataBlock } ` ;
672+ }
673+
674+ function updatePrBody ( prNumber , body ) {
675+ return gh (
676+ [
677+ "api" ,
678+ `repos/Azure/azure-sdk-for-python/pulls/${ prNumber } ` ,
679+ "--method" ,
680+ "PATCH" ,
681+ "--field" ,
682+ `body=${ body } ` ,
683+ ] ,
684+ { check : false , capture : true } ,
685+ ) ;
686+ }
687+
688+ function ensurePrBodySyncMetadata ( pr , metadataBlock ) {
689+ if ( ! metadataBlock || ! pr || ! Number . isInteger ( pr . number ) ) {
690+ return ;
691+ }
692+
693+ const desiredBody = replaceSyncMetadataBlock ( pr . body || "" , metadataBlock ) ;
694+ if ( desiredBody === ( pr . body || "" ) ) {
695+ return ;
696+ }
697+
698+ const result = updatePrBody ( pr . number , desiredBody ) ;
699+ if ( result . status === 0 ) {
700+ logInfo ( `Updated API review sync metadata on PR #${ pr . number } .` ) ;
701+ return ;
702+ }
703+
704+ const details = [
705+ result . stderr ? `stderr: ${ result . stderr . replace ( / \r ? \n / g, " " ) . trim ( ) } ` : "" ,
706+ result . stdout ? `stdout: ${ result . stdout . replace ( / \r ? \n / g, " " ) . trim ( ) } ` : "" ,
707+ ]
708+ . filter ( Boolean )
709+ . join ( "\n " ) ;
710+ logWarning ( `WARNING: failed to update API review sync metadata on PR #${ pr . number } .` + ( details ? `\n ${ details } ` : "" ) ) ;
711+ }
712+
596713function findOpenPrForHead ( headSelector ) {
597714 const { owner, branch } = branchReferenceParts ( headSelector ) ;
598715 const selector = `${ owner } :${ branch } ` ;
@@ -684,7 +801,7 @@ function findOpenPrForBranches(baseBranch, headBranch) {
684801 "--state" ,
685802 "open" ,
686803 "--json" ,
687- "number,url,state,updatedAt" ,
804+ "number,url,state,updatedAt,body " ,
688805 "--limit" ,
689806 "20" ,
690807 ] ,
@@ -707,7 +824,7 @@ function findOpenPrForBranches(baseBranch, headBranch) {
707824 "--search" ,
708825 `repo:Azure/azure-sdk-for-python is:pr is:open head:${ headBranch } base:${ baseBranch } ` ,
709826 "--json" ,
710- "number,url,state,updatedAt" ,
827+ "number,url,state,updatedAt,body " ,
711828 "--limit" ,
712829 "20" ,
713830 ] ,
@@ -979,19 +1096,31 @@ async function main() {
9791096 const workingSelector = args . target || "main" ;
9801097 const workingReference = targetReferenceInfo ( workingSelector ) ;
9811098 const baselineRef = baselineReferenceMarkdown ( args . base ) ;
982-
983- const body = [
984- `Automated API review PR for ${ args . packageName } .` ,
985- "" ,
986- `- **${ workingReference . label } :** ${ workingReference . markdown } (version ${ targetVersion } )` ,
987- `- **Baseline:** ${ baselineRef } (version ${ baseVersion } )` ,
988- "" ,
989- "Generated by scripts/api_md_workflow/create_api_review_pr.js." ,
990- ] . join ( "\n" ) ;
1099+ const syncMetadata = buildSyncMetadataObject ( {
1100+ packageName : args . packageName ,
1101+ packageDir,
1102+ baseBranch,
1103+ reviewBranch,
1104+ headSelector : args . target ,
1105+ } ) ;
1106+ const syncMetadataBlock = buildSyncMetadataBlock ( syncMetadata ) ;
1107+
1108+ const body = replaceSyncMetadataBlock (
1109+ [
1110+ `Automated API review PR for ${ args . packageName } .` ,
1111+ "" ,
1112+ `- **${ workingReference . label } :** ${ workingReference . markdown } (version ${ targetVersion } )` ,
1113+ `- **Baseline:** ${ baselineRef } (version ${ baseVersion } )` ,
1114+ "" ,
1115+ "Generated by scripts/api_md_workflow/create_api_review_pr.js." ,
1116+ ] . join ( "\n" ) ,
1117+ syncMetadataBlock ,
1118+ ) ;
9911119
9921120 if ( baseSelection . reused && reviewSelection . reused ) {
9931121 const existingPr = findOpenPrForBranches ( baseBranch , reviewBranch ) ;
9941122 if ( existingPr ) {
1123+ ensurePrBodySyncMetadata ( existingPr , syncMetadataBlock ) ;
9951124 logInfo ( `\n=== Reusing existing PR #${ existingPr . number } ===` ) ;
9961125 logInfo ( existingPr . url ) ;
9971126 return 0 ;
@@ -1009,6 +1138,7 @@ async function main() {
10091138 } else {
10101139 const existingPr = findOpenPrForBranches ( baseBranch , reviewBranch ) ;
10111140 if ( existingPr ) {
1141+ ensurePrBodySyncMetadata ( existingPr , syncMetadataBlock ) ;
10121142 logInfo ( `\n=== Reusing existing PR #${ existingPr . number } ===` ) ;
10131143 logInfo ( existingPr . url ) ;
10141144 return 0 ;
@@ -1057,6 +1187,9 @@ if (require.main === module) {
10571187 gh = ghRunner ;
10581188 }
10591189 } ,
1190+ buildSyncMetadataBlock,
1191+ buildSyncMetadataObject,
1192+ replaceSyncMetadataBlock,
10601193 targetReferenceInfo,
10611194 } ;
10621195}
0 commit comments