Skip to content

Commit 4003f29

Browse files
committed
feat: progressive comment updates
1 parent 5bc7cef commit 4003f29

File tree

5 files changed

+333
-56
lines changed

5 files changed

+333
-56
lines changed

dist/index.js

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89300,10 +89300,62 @@ var __importStar = (this && this.__importStar) || (function () {
8930089300
};
8930189301
})();
8930289302
Object.defineProperty(exports, "__esModule", ({ value: true }));
89303+
exports.postInitialComment = postInitialComment;
89304+
exports.updateComment = updateComment;
8930389305
exports.postComment = postComment;
8930489306
const github = __importStar(__nccwpck_require__(75380));
8930589307
const logger_1 = __nccwpck_require__(61661);
8930689308
const COMMENT_MARKER = '<!-- auto-pr-screenshots -->';
89309+
async function postInitialComment(options) {
89310+
const { token, context } = options;
89311+
// Only post comments on pull requests
89312+
if (context.eventName !== 'pull_request' || !context.payload.pull_request) {
89313+
logger_1.commentLogger.warn('Not in a pull request context, skipping comment');
89314+
return null;
89315+
}
89316+
const octokit = github.getOctokit(token);
89317+
const { owner, repo } = context.repo;
89318+
const prNumber = context.payload.pull_request.number;
89319+
const commitSha = context.payload.pull_request.head.sha.substring(0, 7);
89320+
logger_1.commentLogger.info(`💬 Posting initial comment to PR #${prNumber}`);
89321+
try {
89322+
const commentBody = generateInitialCommentBody(commitSha);
89323+
const { data: comment } = await octokit.rest.issues.createComment({
89324+
owner,
89325+
repo,
89326+
issue_number: prNumber,
89327+
body: commentBody,
89328+
});
89329+
logger_1.commentLogger.success('✅ Created initial comment');
89330+
return comment.id;
89331+
}
89332+
catch (error) {
89333+
logger_1.commentLogger.error('Failed to post initial comment:', error instanceof Error ? error.message : String(error));
89334+
return null;
89335+
}
89336+
}
89337+
async function updateComment(commentId, screenshots, errors, options, status) {
89338+
const { token, context, config } = options;
89339+
if (context.eventName !== 'pull_request' || !context.payload.pull_request) {
89340+
return;
89341+
}
89342+
const octokit = github.getOctokit(token);
89343+
const { owner, repo } = context.repo;
89344+
const commitSha = context.payload.pull_request.head.sha.substring(0, 7);
89345+
try {
89346+
const commentBody = generateCommentBody(screenshots, errors, context, config, options.showAttribution, status, commitSha);
89347+
await octokit.rest.issues.updateComment({
89348+
owner,
89349+
repo,
89350+
comment_id: commentId,
89351+
body: commentBody,
89352+
});
89353+
logger_1.commentLogger.success(`✅ Updated comment (${status})`);
89354+
}
89355+
catch (error) {
89356+
logger_1.commentLogger.error('Failed to update comment:', error instanceof Error ? error.message : String(error));
89357+
}
89358+
}
8930789359
async function postComment(screenshots, errors, options) {
8930889360
const { token, context, config } = options;
8930989361
// Only post comments on pull requests
@@ -89351,12 +89403,25 @@ async function postComment(screenshots, errors, options) {
8935189403
throw error;
8935289404
}
8935389405
}
89354-
function generateCommentBody(screenshots, errors, context, config, showAttribution = false) {
89406+
function generateInitialCommentBody(commitSha) {
89407+
let body = `${COMMENT_MARKER}\n`;
89408+
body += '## 📸 Auto PR Screenshots\n\n';
89409+
body += `🔄 Screenshot capture has started for commit \`${commitSha}\`\n\n`;
89410+
body += '*Capturing screenshots...*';
89411+
return body;
89412+
}
89413+
function generateCommentBody(screenshots, errors, context, config, showAttribution = false, status = 'complete', commitSha) {
8935589414
const timestamp = new Date().toISOString();
8935689415
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
8935789416
let body = `${COMMENT_MARKER}\n`;
8935889417
body += '## 📸 Auto PR Screenshots\n\n';
89359-
body += `*Updated: ${timestamp}*\n\n`;
89418+
if (status === 'in_progress') {
89419+
body += `🔄 Screenshot capture in progress for commit \`${commitSha}\`\n\n`;
89420+
}
89421+
else {
89422+
body += `✅ Screenshot capture complete for commit \`${commitSha}\`\n\n`;
89423+
body += `*Completed: ${timestamp}*\n\n`;
89424+
}
8936089425
if (screenshots.length === 0 && errors.length === 0) {
8936189426
body += `⚠️ No screenshots were captured. Check the [action logs](${runUrl}) for details.\n`;
8936289427
return body;
@@ -90004,9 +90069,49 @@ async function run() {
9000490069
if (context.eventName !== 'pull_request' && !process.env.LOCAL_TEST) {
9000590070
logger_1.logger.warn('⚠️ Not running in a pull request context, some features may be limited');
9000690071
}
90007-
// Capture screenshots
90072+
// Post initial comment
90073+
let commentId = null;
90074+
if (!skipComment && context.eventName === 'pull_request') {
90075+
commentId = await (0, comment_poster_1.postInitialComment)({
90076+
token,
90077+
context,
90078+
config,
90079+
showAttribution,
90080+
});
90081+
}
90082+
// Track uploaded screenshots to avoid re-uploading
90083+
const uploadedScreenshots = [];
90084+
let lastUploadedCount = 0;
90085+
// Capture screenshots with progress updates
9000890086
logger_1.logger.info('📸 Capturing screenshots...');
90009-
const captureResult = await (0, screenshot_capture_1.captureScreenshots)(config, { browsers });
90087+
const captureResult = await (0, screenshot_capture_1.captureScreenshots)(config, {
90088+
browsers,
90089+
onProgress: async (progress) => {
90090+
// Only upload and update if we have more than one screenshot configured
90091+
if (config.screenshots.length <= 1)
90092+
return;
90093+
// Upload any new screenshots
90094+
const newScreenshots = progress.successful.slice(lastUploadedCount);
90095+
if (newScreenshots.length > 0) {
90096+
const newUploaded = await (0, screenshot_uploader_1.uploadScreenshots)(newScreenshots, {
90097+
branch,
90098+
token,
90099+
context,
90100+
});
90101+
uploadedScreenshots.push(...newUploaded);
90102+
lastUploadedCount = progress.successful.length;
90103+
}
90104+
// Update comment with progress
90105+
if (commentId && !skipComment) {
90106+
await (0, comment_poster_1.updateComment)(commentId, uploadedScreenshots, progress.failed, {
90107+
token,
90108+
context,
90109+
config,
90110+
showAttribution,
90111+
}, 'in_progress');
90112+
}
90113+
},
90114+
});
9001090115
const screenshots = captureResult.successful;
9001190116
const screenshotErrors = captureResult.failed;
9001290117
if (screenshotErrors.length > 0) {
@@ -90021,38 +90126,40 @@ async function run() {
9002190126
}
9002290127
else {
9002390128
logger_1.logger.warn('⚠️ Continuing despite no screenshots (fail-on-error is false)');
90024-
// Post comment with just errors if we're in a PR context
90025-
if (!skipComment && context.eventName === 'pull_request') {
90026-
logger_1.logger.info('💬 Posting error comment to PR...');
90027-
await (0, comment_poster_1.postComment)([], screenshotErrors, {
90129+
// Update comment with just errors if we're in a PR context
90130+
if (commentId && !skipComment) {
90131+
await (0, comment_poster_1.updateComment)(commentId, [], screenshotErrors, {
9002890132
token,
9002990133
context,
9003090134
config,
9003190135
showAttribution,
90032-
});
90033-
logger_1.logger.success('✅ Error comment posted');
90136+
}, 'complete');
9003490137
}
9003590138
return; // Exit early but don't fail
9003690139
}
9003790140
}
9003890141
logger_1.logger.success(`✅ Captured ${screenshots.length} screenshot(s)`);
90039-
// Upload to branch
90040-
logger_1.logger.info(`📤 Uploading screenshots to branch: ${branch}`);
90041-
const uploadedUrls = await (0, screenshot_uploader_1.uploadScreenshots)(screenshots, {
90042-
branch,
90043-
token,
90044-
context,
90045-
});
90046-
// Post comment to PR
90047-
if (!skipComment && context.eventName === 'pull_request') {
90048-
logger_1.logger.info('💬 Posting comment to PR...');
90049-
await (0, comment_poster_1.postComment)(uploadedUrls, screenshotErrors, {
90142+
// Upload any remaining screenshots that weren't uploaded during progress
90143+
const remainingScreenshots = screenshots.slice(lastUploadedCount);
90144+
if (remainingScreenshots.length > 0) {
90145+
logger_1.logger.info(`📤 Uploading ${remainingScreenshots.length} remaining screenshot(s) to branch: ${branch}`);
90146+
const remainingUploaded = await (0, screenshot_uploader_1.uploadScreenshots)(remainingScreenshots, {
90147+
branch,
90148+
token,
90149+
context,
90150+
});
90151+
uploadedScreenshots.push(...remainingUploaded);
90152+
}
90153+
// Post final comment update
90154+
if (commentId && !skipComment) {
90155+
logger_1.logger.info('💬 Updating comment with final results...');
90156+
await (0, comment_poster_1.updateComment)(commentId, uploadedScreenshots, screenshotErrors, {
9005090157
token,
9005190158
context,
9005290159
config,
9005390160
showAttribution,
90054-
});
90055-
logger_1.logger.success('✅ Comment posted successfully');
90161+
}, 'complete');
90162+
logger_1.logger.success('✅ Comment updated successfully');
9005690163
}
9005790164
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
9005890165
logger_1.logger.success(`🎉 Auto PR Screenshots completed in ${duration}s`);
@@ -90384,7 +90491,7 @@ const BROWSER_MAP = {
9038490491
webkit: playwright_1.webkit,
9038590492
};
9038690493
async function captureScreenshots(config, options = {}) {
90387-
const { browsers = 'chromium' } = options;
90494+
const { browsers = 'chromium', onProgress } = options;
9038890495
const browserList = browsers.split(',').map((b) => b.trim());
9038990496
logger_1.captureLogger.info(`Starting screenshot capture with browsers: ${browserList.join(', ')}`);
9039090497
const screenshotsDir = path.join(process.cwd(), 'screenshots');
@@ -90409,6 +90516,10 @@ async function captureScreenshots(config, options = {}) {
9040990516
else {
9041090517
failed.push(result.error);
9041190518
}
90519+
// Call progress callback after each screenshot
90520+
if (onProgress) {
90521+
await onProgress({ successful: [...successful], failed: [...failed] });
90522+
}
9041290523
}
9041390524
}
9041490525
finally {

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/comment-poster.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,84 @@ interface CommentOptions {
1111

1212
const COMMENT_MARKER = '<!-- auto-pr-screenshots -->';
1313

14+
export async function postInitialComment(options: CommentOptions): Promise<number | null> {
15+
const { token, context } = options;
16+
17+
// Only post comments on pull requests
18+
if (context.eventName !== 'pull_request' || !context.payload.pull_request) {
19+
logger.warn('Not in a pull request context, skipping comment');
20+
return null;
21+
}
22+
23+
const octokit = github.getOctokit(token);
24+
const { owner, repo } = context.repo;
25+
const prNumber = context.payload.pull_request.number;
26+
const commitSha = context.payload.pull_request.head.sha.substring(0, 7);
27+
28+
logger.info(`💬 Posting initial comment to PR #${prNumber}`);
29+
30+
try {
31+
const commentBody = generateInitialCommentBody(commitSha);
32+
const { data: comment } = await octokit.rest.issues.createComment({
33+
owner,
34+
repo,
35+
issue_number: prNumber,
36+
body: commentBody,
37+
});
38+
logger.success('✅ Created initial comment');
39+
return comment.id;
40+
} catch (error) {
41+
logger.error(
42+
'Failed to post initial comment:',
43+
error instanceof Error ? error.message : String(error),
44+
);
45+
return null;
46+
}
47+
}
48+
49+
export async function updateComment(
50+
commentId: number,
51+
screenshots: UploadedScreenshot[],
52+
errors: ScreenshotError[],
53+
options: CommentOptions,
54+
status: 'in_progress' | 'complete',
55+
): Promise<void> {
56+
const { token, context, config } = options;
57+
58+
if (context.eventName !== 'pull_request' || !context.payload.pull_request) {
59+
return;
60+
}
61+
62+
const octokit = github.getOctokit(token);
63+
const { owner, repo } = context.repo;
64+
const commitSha = context.payload.pull_request.head.sha.substring(0, 7);
65+
66+
try {
67+
const commentBody = generateCommentBody(
68+
screenshots,
69+
errors,
70+
context,
71+
config,
72+
options.showAttribution,
73+
status,
74+
commitSha,
75+
);
76+
77+
await octokit.rest.issues.updateComment({
78+
owner,
79+
repo,
80+
comment_id: commentId,
81+
body: commentBody,
82+
});
83+
logger.success(`✅ Updated comment (${status})`);
84+
} catch (error) {
85+
logger.error(
86+
'Failed to update comment:',
87+
error instanceof Error ? error.message : String(error),
88+
);
89+
}
90+
}
91+
1492
export async function postComment(
1593
screenshots: UploadedScreenshot[],
1694
errors: ScreenshotError[],
@@ -76,19 +154,35 @@ export async function postComment(
76154
}
77155
}
78156

157+
function generateInitialCommentBody(commitSha: string): string {
158+
let body = `${COMMENT_MARKER}\n`;
159+
body += '## 📸 Auto PR Screenshots\n\n';
160+
body += `🔄 Screenshot capture has started for commit \`${commitSha}\`\n\n`;
161+
body += '*Capturing screenshots...*';
162+
return body;
163+
}
164+
79165
function generateCommentBody(
80166
screenshots: UploadedScreenshot[],
81167
errors: ScreenshotError[],
82168
context: typeof github.context,
83169
config: Config,
84170
showAttribution: boolean = false,
171+
status: 'in_progress' | 'complete' = 'complete',
172+
commitSha?: string,
85173
): string {
86174
const timestamp = new Date().toISOString();
87175
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
88176

89177
let body = `${COMMENT_MARKER}\n`;
90178
body += '## 📸 Auto PR Screenshots\n\n';
91-
body += `*Updated: ${timestamp}*\n\n`;
179+
180+
if (status === 'in_progress') {
181+
body += `🔄 Screenshot capture in progress for commit \`${commitSha}\`\n\n`;
182+
} else {
183+
body += `✅ Screenshot capture complete for commit \`${commitSha}\`\n\n`;
184+
body += `*Completed: ${timestamp}*\n\n`;
185+
}
92186

93187
if (screenshots.length === 0 && errors.length === 0) {
94188
body += `⚠️ No screenshots were captured. Check the [action logs](${runUrl}) for details.\n`;

0 commit comments

Comments
 (0)