907907 logger . info ( `📋 Found ${ buddyPRs . length } buddy-bot PR(s)` )
908908
909909 let rebasedCount = 0
910+ let closedCount = 0
910911
911912 for ( const pr of buddyPRs ) {
913+ // First, check if this PR should be auto-closed due to respectLatest config changes
914+ const shouldAutoClose = await checkForAutoClose ( pr , config , logger )
915+ if ( shouldAutoClose ) {
916+ if ( options . dryRun ) {
917+ logger . info ( `🔍 [DRY RUN] Would auto-close PR #${ pr . number } (contains dynamic versions that are now filtered)` )
918+
919+ // Extract package names for logging
920+ const packageNames = extractPackageNamesFromPRBody ( pr . body )
921+ logger . info ( `📋 PR contains packages: ${ packageNames . join ( ', ' ) } ` )
922+ closedCount ++
923+ continue
924+ }
925+ else {
926+ logger . info ( `🔒 Auto-closing PR #${ pr . number } (contains dynamic versions that are now filtered by respectLatest config)` )
927+
928+ // Extract package names for logging
929+ const packageNames = extractPackageNamesFromPRBody ( pr . body )
930+ logger . info ( `📋 PR contains packages: ${ packageNames . join ( ', ' ) } ` )
931+
932+ try {
933+ await gitProvider . closePullRequest ( pr . number )
934+ logger . success ( `✅ Successfully closed PR #${ pr . number } (contains dynamic versions that are now filtered by respectLatest configuration)` )
935+ closedCount ++
936+ continue
937+ }
938+ catch ( error ) {
939+ logger . error ( `❌ Failed to close PR #${ pr . number } :` , error )
940+ }
941+ }
942+ }
943+
912944 // Check if rebase checkbox is checked
913945 const isRebaseChecked = checkRebaseCheckbox ( pr . body )
914946
@@ -994,11 +1026,16 @@ cli
9941026 }
9951027 }
9961028
1029+ if ( closedCount > 0 ) {
1030+ logger . success ( `🔒 ${ options . dryRun ? 'Would close' : 'Successfully closed' } ${ closedCount } PR(s) with dynamic versions` )
1031+ }
1032+
9971033 if ( rebasedCount > 0 ) {
9981034 logger . success ( `✅ ${ options . dryRun ? 'Would rebase' : 'Successfully rebased' } ${ rebasedCount } PR(s)` )
9991035 }
1000- else {
1001- logger . info ( '✅ No PRs need rebasing' )
1036+
1037+ if ( closedCount === 0 && rebasedCount === 0 ) {
1038+ logger . info ( '✅ No PRs need attention' )
10021039 }
10031040 }
10041041 catch ( error ) {
@@ -1014,6 +1051,87 @@ function checkRebaseCheckbox(body: string): boolean {
10141051 return checkedPattern . test ( body )
10151052}
10161053
1054+ // Helper function to extract package names from PR body
1055+ function extractPackageNamesFromPRBody ( body : string ) : string [ ] {
1056+ const packages : string [ ] = [ ]
1057+
1058+ // Look for package names in the PR body table
1059+ const tableMatch = body . match ( / \| [ ^ | ] * \| [ ^ | ] * \| [ ^ | ] * \| [ ^ | ] * \| / g)
1060+ if ( tableMatch ) {
1061+ for ( const row of tableMatch ) {
1062+ // Extract package name from table row - use a more specific pattern to avoid backtracking
1063+ const packageMatch = row . match ( / \[ ( [ ^ \] ] + ) \] \( [ ^ ) ] * \) / )
1064+ if ( packageMatch ) {
1065+ packages . push ( packageMatch [ 1 ] )
1066+ }
1067+ }
1068+ }
1069+
1070+ // Also look for package names in the release notes section
1071+ const releaseNotesMatch = body . match ( / < s u m m a r y > ( [ ^ < ] + ) < \/ s u m m a r y > / g)
1072+ if ( releaseNotesMatch ) {
1073+ for ( const match of releaseNotesMatch ) {
1074+ const packageName = match . replace ( / < s u m m a r y > / , '' ) . replace ( / < \/ s u m m a r y > / , '' ) . trim ( )
1075+ if ( packageName && ! packages . includes ( packageName ) ) {
1076+ packages . push ( packageName )
1077+ }
1078+ }
1079+ }
1080+
1081+ return packages
1082+ }
1083+
1084+ // Helper function to check if a PR should be auto-closed due to respectLatest config changes
1085+ async function checkForAutoClose ( pr : any , config : any , _logger : any ) : Promise < boolean > {
1086+ const respectLatest = config . packages ?. respectLatest ?? true
1087+
1088+ // Only auto-close if respectLatest is true (the new default behavior)
1089+ if ( ! respectLatest ) {
1090+ return false
1091+ }
1092+
1093+ // Check if the existing PR contains updates that would now be filtered out
1094+ // Look for dynamic version indicators in the PR body
1095+ const dynamicIndicators = [ 'latest' , '*' , 'main' , 'master' , 'develop' , 'dev' ]
1096+ const prBody = pr . body . toLowerCase ( )
1097+
1098+ // Check if PR body contains dynamic version indicators
1099+ const hasDynamicVersions = dynamicIndicators . some ( indicator => prBody . includes ( indicator ) )
1100+ if ( ! hasDynamicVersions ) {
1101+ return false
1102+ }
1103+
1104+ // Extract packages from PR body and check if they have dynamic versions
1105+ const packageNames = extractPackageNamesFromPRBody ( pr . body )
1106+
1107+ // Check if any of the packages in the PR had dynamic versions
1108+ const packagesWithDynamicVersions = packageNames . filter ( ( pkg ) => {
1109+ // Look for the package in the PR body table format: | [package](url) | version → newVersion |
1110+ const packagePattern = new RegExp ( `\\|\\s*\\[${ pkg . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) } \\]\\([^)]+\\)\\s*\\|\\s*([^|]+)\\s*\\|` , 'i' )
1111+ const match = pr . body . match ( packagePattern )
1112+ if ( ! match ) {
1113+ // Fallback: look for any version pattern with this package
1114+ const fallbackPattern = new RegExp ( `${ pkg . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) } [^\\w]*[:=]\\s*["']?([^"'\n]+)["']?` , 'i' )
1115+ const fallbackMatch = pr . body . match ( fallbackPattern )
1116+ if ( ! fallbackMatch )
1117+ return false
1118+
1119+ const version = fallbackMatch [ 1 ] . toLowerCase ( ) . trim ( )
1120+ return dynamicIndicators . includes ( version )
1121+ }
1122+
1123+ const versionChange = match [ 1 ] . trim ( )
1124+ const currentVersionMatch = versionChange . match ( / ^ ( [ ^ → ] + ) → / )
1125+ if ( ! currentVersionMatch )
1126+ return false
1127+
1128+ const currentVersion = currentVersionMatch [ 1 ] . trim ( ) . toLowerCase ( )
1129+ return dynamicIndicators . includes ( currentVersion )
1130+ } )
1131+
1132+ return packagesWithDynamicVersions . length > 0
1133+ }
1134+
10171135cli
10181136 . command ( 'check <packages...>' , 'Check specific packages for updates' )
10191137 . option ( '--verbose, -v' , 'Enable verbose logging' )
0 commit comments