Skip to content

Commit 23c7c8c

Browse files
awaseemcharislam
andauthored
Fix: updated action to include state (supabase#40385)
* updated action to include state * removed CI status * Update .github/workflows/dashboard-pr-reminder.yml Co-authored-by: Charis <[email protected]> --------- Co-authored-by: Charis <[email protected]>
1 parent 319c889 commit 23c7c8c

File tree

3 files changed

+331
-182
lines changed

3 files changed

+331
-182
lines changed

.github/workflows/dashboard-pr-reminder.yml

Lines changed: 7 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -16,114 +16,16 @@ jobs:
1616
check-dashboard-prs:
1717
runs-on: ubuntu-latest
1818
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
21+
1922
- name: Find Dashboard PRs older than 24 hours
2023
id: find-prs
2124
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
2225
with:
2326
script: |
24-
const TWENTY_FOUR_HOURS_AGO = new Date(Date.now() - 24 * 60 * 60 * 1000);
25-
const DASHBOARD_PATH = 'apps/studio/';
26-
27-
console.log(`Looking for PRs older than: ${TWENTY_FOUR_HOURS_AGO.toISOString()}`);
28-
29-
const stalePRs = [];
30-
let page = 1;
31-
let hasMore = true;
32-
33-
// Fetch PRs page by page, newest first
34-
while (hasMore && page <= 10) { // Limit to 10 pages (1000 PRs) as safety measure
35-
console.log(`Fetching page ${page}...`);
36-
37-
const { data: prs } = await github.rest.pulls.list({
38-
owner: context.repo.owner,
39-
repo: context.repo.repo,
40-
state: 'open',
41-
sort: 'created',
42-
direction: 'desc',
43-
per_page: 100,
44-
page: page
45-
});
46-
47-
if (prs.length === 0) {
48-
hasMore = false;
49-
break;
50-
}
51-
52-
// Check each PR
53-
for (const pr of prs) {
54-
// Skip PRs from forks - only check internal PRs
55-
if (pr.head.repo && pr.head.repo.full_name !== context.repo.owner + '/' + context.repo.repo) {
56-
console.log(`PR #${pr.number} is from a fork, skipping...`);
57-
continue;
58-
}
59-
60-
// Skip dependabot PRs
61-
if (pr.user.login === 'dependabot[bot]' || pr.user.login === 'dependabot') {
62-
console.log(`PR #${pr.number} is from dependabot, skipping...`);
63-
continue;
64-
}
65-
66-
// Skip draft PRs
67-
if (pr.draft) {
68-
console.log(`PR #${pr.number} is a draft, skipping...`);
69-
continue;
70-
}
71-
72-
const createdAt = new Date(pr.created_at);
73-
74-
// If this PR is newer than 24 hours, skip it
75-
if (createdAt > TWENTY_FOUR_HOURS_AGO) {
76-
console.log(`PR #${pr.number} is too new, skipping...`);
77-
continue;
78-
}
79-
80-
// If we've reached PRs older than what we're checking, we can stop
81-
// But we still need to check if they touch Dashboard files
82-
console.log(`Checking PR #${pr.number}: ${pr.title}`);
83-
84-
// Fetch files changed in this PR
85-
const { data: files } = await github.rest.pulls.listFiles({
86-
owner: context.repo.owner,
87-
repo: context.repo.repo,
88-
pull_number: pr.number,
89-
per_page: 100
90-
});
91-
92-
// Check if any file is under apps/studio/
93-
const touchesDashboard = files.some(file => file.filename.startsWith(DASHBOARD_PATH));
94-
95-
if (touchesDashboard) {
96-
const hoursOld = Math.floor((Date.now() - createdAt.getTime()) / (1000 * 60 * 60));
97-
const daysOld = Math.floor(hoursOld / 24);
98-
99-
stalePRs.push({
100-
number: pr.number,
101-
title: pr.title,
102-
url: pr.html_url,
103-
author: pr.user.login,
104-
createdAt: pr.created_at,
105-
hoursOld: hoursOld,
106-
daysOld: daysOld,
107-
fileCount: files.filter(f => f.filename.startsWith(DASHBOARD_PATH)).length
108-
});
109-
110-
console.log(`✓ Found stale Dashboard PR #${pr.number}`);
111-
}
112-
}
113-
114-
page++;
115-
}
116-
117-
console.log(`Found ${stalePRs.length} stale Dashboard PRs`);
118-
119-
// Sort by age (oldest first)
120-
stalePRs.sort((a, b) => a.hoursOld - b.hoursOld);
121-
122-
// Store results for next step
123-
core.setOutput('stale_prs', JSON.stringify(stalePRs));
124-
core.setOutput('count', stalePRs.length);
125-
126-
return stalePRs;
27+
const findStalePRs = require('./scripts/actions/find-stale-dashboard-prs.js');
28+
return await findStalePRs({ github, context, core });
12729
12830
- name: Send Slack notification
12931
if: fromJSON(steps.find-prs.outputs.count) > 0
@@ -133,87 +35,10 @@ jobs:
13335
STALE_PRS_JSON: ${{ steps.find-prs.outputs.stale_prs }}
13436
with:
13537
script: |
38+
const sendSlackNotification = require('./scripts/actions/send-slack-pr-notification.js');
13639
const stalePRs = JSON.parse(process.env.STALE_PRS_JSON);
137-
const count = stalePRs.length;
138-
139-
// Build PR blocks with proper escaping for Slack mrkdwn
140-
const prBlocks = stalePRs.map(pr => {
141-
// Format age display
142-
const remainingHours = pr.hoursOld % 24;
143-
const ageText = pr.daysOld > 0
144-
? `${pr.daysOld}d ${remainingHours}h`
145-
: `${pr.hoursOld}h`;
146-
147-
// Escape special characters for Slack mrkdwn (escape &, <, >)
148-
const escapeSlack = (text) => {
149-
return text
150-
.replace(/&/g, '&amp;')
151-
.replace(/</g, '&lt;')
152-
.replace(/>/g, '&gt;');
153-
};
154-
155-
// Truncate title if too long (max 3000 chars for entire text field)
156-
const maxTitleLength = 200;
157-
const safeTitle = pr.title.length > maxTitleLength
158-
? escapeSlack(pr.title.substring(0, maxTitleLength) + '...')
159-
: escapeSlack(pr.title);
160-
161-
return {
162-
type: "section",
163-
text: {
164-
type: "mrkdwn",
165-
text: `*<${pr.url}|#${pr.number}: ${safeTitle}>*\n:bust_in_silhouette: @${pr.author} • :clock3: ${ageText} old • :file_folder: ${pr.fileCount} Dashboard files`
166-
}
167-
};
168-
});
169-
170-
// Slack has a 50 block limit, we use 3 for header/intro/divider
171-
// So we can show max 47 PRs
172-
const MAX_PRS_TO_SHOW = 47;
173-
const prBlocksToShow = prBlocks.slice(0, MAX_PRS_TO_SHOW);
174-
const hasMorePRs = prBlocks.length > MAX_PRS_TO_SHOW;
175-
176-
// Build complete Slack message
177-
const slackMessage = {
178-
text: "Dashboard PRs needing attention",
179-
blocks: [
180-
{
181-
type: "header",
182-
text: {
183-
type: "plain_text",
184-
text: "Dashboard PRs Older Than 24 Hours"
185-
}
186-
},
187-
{
188-
type: "section",
189-
text: {
190-
type: "mrkdwn",
191-
text: `There are *${count}* open PRs affecting /apps/studio/ that are older than 24 hours:${hasMorePRs ? ` (showing first ${MAX_PRS_TO_SHOW})` : ''}`
192-
}
193-
},
194-
{
195-
type: "divider"
196-
},
197-
...prBlocksToShow
198-
]
199-
};
200-
201-
// Send to Slack
20240
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
203-
const response = await fetch(webhookUrl, {
204-
method: 'POST',
205-
headers: {
206-
'Content-Type': 'application/json',
207-
},
208-
body: JSON.stringify(slackMessage)
209-
});
210-
211-
if (!response.ok) {
212-
const errorText = await response.text();
213-
throw new Error(`Slack notification failed: ${response.status} ${response.statusText}\n${errorText}`);
214-
}
215-
216-
console.log('✓ Slack notification sent successfully!');
41+
await sendSlackNotification(stalePRs, webhookUrl);
21742
21843
- name: No stale PRs found
21944
if: fromJSON(steps.find-prs.outputs.count) == 0

0 commit comments

Comments
 (0)