@@ -9,10 +9,16 @@ const LOOKBACK_DAYS = parseInt(process.env.LOOKBACK_DAYS ?? '2');
99const START_HOUR_UTC = parseInt ( process . env . START_HOUR_UTC ?? '7' ) ;
1010
1111const START_MINUTE_UTC = 0 ;
12- const RELEVANT_TITLE_REGEX = / ^ ( f e a t | p e r f ) ( \( | : | ! ) | \b b u m p \b / i;
12+ const RELEVANT_TITLE_REGEX = / ^ ( f e a t | p e r f | f i x ) \s * ( \( | : | ! | \/ ) | \b b u m p \b / i;
1313const TEAM_LABEL_PREFIX = 'team-' ;
1414const SIZE_LABEL_PREFIX = 'size-' ;
15-
15+ const AUTOMATED_TEST_PATTERNS = [
16+ / \. t e s t \. ( j s | t s | t s x ) $ / ,
17+ / \. s p e c \. ( j s | t s | t s x ) $ / ,
18+ / ( ^ | \/ ) t e s t \/ / ,
19+ / ( ^ | \/ ) e 2 e \/ / ,
20+ / ( ^ | \/ ) w d i o \/ /
21+ ] ;
1622
1723if ( ! githubToken ) throw new Error ( 'Missing GITHUB_TOKEN env var' ) ;
1824if ( ! spreadsheetId ) throw new Error ( 'Missing SHEET_ID env var' ) ;
@@ -57,6 +63,7 @@ function headerRowFor(type) {
5763 'Merged Time (UTC)' ,
5864 'Author' ,
5965 'PR Size' ,
66+ 'Auto Tests' ,
6067 'Team Responsible' ,
6168 colF ,
6269 colG ,
@@ -118,7 +125,7 @@ async function createSheetFromTemplateOrBlank(authClient, sheetsList, title, pla
118125 await sheets . spreadsheets . values . update ( {
119126 spreadsheetId,
120127 auth : authClient ,
121- range : `${ title } !A2:H2 ` ,
128+ range : `${ title } !A2:I2 ` ,
122129 valueInputOption : 'USER_ENTERED' ,
123130 requestBody : { values : [ headerRowFor ( platformType ) ] } ,
124131 } ) ;
@@ -174,7 +181,7 @@ async function createSheetFromTemplateOrBlank(authClient, sheetsList, title, pla
174181 await sheets . spreadsheets . values . update ( {
175182 spreadsheetId,
176183 auth : authClient ,
177- range : `${ title } !A2:H2 ` ,
184+ range : `${ title } !A2:I2 ` ,
178185 valueInputOption : 'USER_ENTERED' ,
179186 requestBody : { values : [ headerRowFor ( platformType ) ] } ,
180187 } ) ;
@@ -201,7 +208,7 @@ async function readRows(authClient, title) {
201208 const res = await sheets . spreadsheets . values . get ( {
202209 spreadsheetId,
203210 auth : authClient ,
204- range : `${ title } !A3:H ` ,
211+ range : `${ title } !A3:I ` ,
205212 } ) ;
206213 return res . data . values || [ ] ;
207214 } catch ( e ) {
@@ -215,7 +222,7 @@ async function appendRows(authClient, title, rows) {
215222 await sheets . spreadsheets . values . append ( {
216223 spreadsheetId,
217224 auth : authClient ,
218- range : `${ title } !A4:H ` ,
225+ range : `${ title } !A4:I ` ,
219226 valueInputOption : 'USER_ENTERED' ,
220227 insertDataOption : 'INSERT_ROWS' ,
221228 requestBody : { values : rows } ,
@@ -288,7 +295,7 @@ async function fetchMergedPRsSince(owner, repo, sinceDateISO) {
288295 order : 'desc' ,
289296 per_page : 100 ,
290297 page,
291- advanced_search : true // ← This stops the deprecation warning
298+ advanced_search : true
292299 } ) ;
293300
294301 if ( ! data . items . length ) break ;
@@ -365,6 +372,9 @@ function splitByReleaseAndTitle(items) {
365372// Add efficient version detection with caching
366373let versionCache = new Map ( ) ; // Cache version bumps per repo
367374
375+ // Automated test detection cache
376+ let automatedTestCache = new Map ( ) ; // Cache automated test results per PR: "owner/repo/prNumber" -> "Yes"|"No"|"Unknown"
377+
368378async function getVersionTimelineForRepo ( owner , repo , sinceDateISO ) {
369379 const cacheKey = `${ owner } /${ repo } ` ;
370380 if ( versionCache . has ( cacheKey ) ) {
@@ -511,11 +521,15 @@ async function buildTabGrouping(owner, repo, relevantItems, sinceDateISO) {
511521 }
512522
513523 for ( const pr of prs ) {
524+ // Check if PR modifies automated test files
525+ const automatedTestsModified = await checkAutomatedTestFiles ( owner , repo , pr . number ) ;
526+
514527 const row = [
515528 makePrHyperlinkCell ( pr . html_url , pr . title , pr . number ) ,
516529 formatDateHumanUTC ( pr . closed_at || '' ) ,
517530 pr . user . login ,
518531 extractSize ( pr . labels || [ ] ) ,
532+ automatedTestsModified ,
519533 extractTeam ( pr . labels || [ ] ) ,
520534 '' ,
521535 '' ,
@@ -537,7 +551,7 @@ async function findVersionBumpCommits(owner, repo, sinceDateISO) {
537551 sha : 'main' ,
538552 since : sinceDateISO ,
539553 path : 'package.json' ,
540- per_page : 50 // Should be enough for 15 days
554+ per_page : 50 // Should be enough for e.g. 15 days lookback
541555 } ) ;
542556
543557 const versionBumps = [ ] ;
@@ -580,6 +594,69 @@ async function findVersionBumpCommits(owner, repo, sinceDateISO) {
580594 }
581595}
582596
597+ // Automated test detection functions
598+ async function fetchPRFiles ( owner , repo , prNumber ) {
599+ try {
600+ const allFiles = [ ] ;
601+ let page = 1 ;
602+
603+ while ( true ) {
604+ const { data : files } = await octokit . rest . pulls . listFiles ( {
605+ owner,
606+ repo,
607+ pull_number : prNumber ,
608+ per_page : 100 ,
609+ page
610+ } ) ;
611+
612+ allFiles . push ( ...files ) ;
613+
614+ // If we got less than 100 files, we've reached the end
615+ if ( files . length < 100 ) break ;
616+
617+ page ++ ;
618+ await sleep ( 100 ) ;
619+ }
620+
621+ return allFiles . map ( file => file . filename ) ;
622+ } catch ( e ) {
623+ console . log ( `⚠️ Failed to fetch files for PR #${ prNumber } : ${ e . message } ` ) ;
624+ return null ; // Return null to indicate API failure
625+ }
626+ }
627+
628+ function checkFilesForAutomatedTestPatterns ( filenames ) {
629+ if ( ! filenames || filenames . length === 0 ) return false ;
630+
631+ // Debug logging to help identify pattern matching issues
632+ const matches = filenames . filter ( filename =>
633+ AUTOMATED_TEST_PATTERNS . some ( pattern => pattern . test ( filename ) )
634+ ) ;
635+
636+ return matches . length > 0 ;
637+ }
638+
639+ async function checkAutomatedTestFiles ( owner , repo , prNumber ) {
640+ const cacheKey = `${ owner } /${ repo } /${ prNumber } ` ;
641+ if ( automatedTestCache . has ( cacheKey ) ) {
642+ return automatedTestCache . get ( cacheKey ) ;
643+ }
644+
645+ const filenames = await fetchPRFiles ( owner , repo , prNumber ) ;
646+ let result ;
647+
648+ if ( filenames === null ) {
649+ result = 'Unknown' ; // API error
650+ } else if ( checkFilesForAutomatedTestPatterns ( filenames ) ) {
651+ result = 'Yes' ;
652+ } else {
653+ result = 'No' ;
654+ }
655+
656+ automatedTestCache . set ( cacheKey , result ) ;
657+ return result ;
658+ }
659+
583660async function processTab ( authClient , title , entries , platformType ) {
584661 const { sheetId, isNew } = await ensureSheetExists ( authClient , title , platformType ) ;
585662 const existing = await readRows ( authClient , title ) ;
0 commit comments