Skip to content

Commit bcf27ba

Browse files
authored
Adapt post validation script to detect if PRs have automated tests (#100)
* ci: adapt the regex to allow whitespaces in the conventional commits to avoid edge cases * ci: adapt the regex to allow whitespaces in the conventional commits to avoid edge cases * ci: adapt the regex to allow whitespaces in the conventional commits to avoid edge cases
1 parent 724c5f4 commit bcf27ba

File tree

1 file changed

+85
-8
lines changed

1 file changed

+85
-8
lines changed

.github/scripts/post-merge-validation-tracker.mjs

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ const LOOKBACK_DAYS = parseInt(process.env.LOOKBACK_DAYS ?? '2');
99
const START_HOUR_UTC = parseInt(process.env.START_HOUR_UTC ?? '7');
1010

1111
const START_MINUTE_UTC = 0;
12-
const RELEVANT_TITLE_REGEX = /^(feat|perf)(\(|:|!)|\bbump\b/i;
12+
const RELEVANT_TITLE_REGEX = /^(feat|perf|fix)\s*(\(|:|!|\/)|\bbump\b/i;
1313
const TEAM_LABEL_PREFIX = 'team-';
1414
const SIZE_LABEL_PREFIX = 'size-';
15-
15+
const AUTOMATED_TEST_PATTERNS = [
16+
/\.test\.(js|ts|tsx)$/,
17+
/\.spec\.(js|ts|tsx)$/,
18+
/(^|\/)test\//,
19+
/(^|\/)e2e\//,
20+
/(^|\/)wdio\//
21+
];
1622

1723
if (!githubToken) throw new Error('Missing GITHUB_TOKEN env var');
1824
if (!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
366373
let 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+
368378
async 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+
583660
async 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

Comments
 (0)