@@ -75,30 +75,125 @@ jobs:
7575 with :
7676 script : |
7777 const previewUrl = '${{ steps.vercel-preview.outputs.preview-url }}';
78- const pr = context.payload.workflow_run.pull_requests && context.payload.workflow_run.pull_requests[0];
79- if (!pr) {
80- core.warning('No pull request metadata available; skipping preview comment.');
78+ const workflowRun = context.payload.workflow_run;
79+ const pr = workflowRun?.pull_requests?.[0];
80+ if (!pr || !previewUrl) {
81+ core.warning('Missing pull request metadata or preview URL; skipping preview success comment.');
8182 return;
8283 }
83- await github.rest.issues.createComment({
84- issue_number: pr.number,
84+
85+ const commentTag = '<!-- vercel-preview-card -->';
86+ const branch = workflowRun.head_branch ?? 'unknown-branch';
87+ const sha = workflowRun.head_sha ?? '';
88+ const shortSha = sha ? sha.slice(0, 7) : 'unknown';
89+ const commitUrl = sha
90+ ? `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${sha}`
91+ : `https://github.com/${context.repo.owner}/${context.repo.repo}`;
92+ const actor = workflowRun.actor ?? 'workflow_run';
93+
94+ const body = [
95+ commentTag,
96+ '✅ **Preview deployment ready**',
97+ '',
98+ '| Field | Value |',
99+ '| --- | --- |',
100+ `| Branch | \`${branch}\` |`,
101+ `| Commit | [${shortSha}](${commitUrl}) |`,
102+ `| Preview | [Open preview](${previewUrl}) |`,
103+ '',
104+ `_Triggered by @${actor}_`
105+ ].join('\n');
106+
107+ const comments = await github.paginate(github.rest.issues.listComments, {
85108 owner: context.repo.owner,
86109 repo: context.repo.repo,
87- body: `✅ Tests passed! Preview deployment ready:\n\n🔗 ${previewUrl}`
110+ issue_number: pr.number,
111+ per_page: 100
88112 });
113+ const existingComment = comments.find((comment) => comment.body?.includes(commentTag));
114+
115+ if (existingComment) {
116+ await github.rest.issues.updateComment({
117+ owner: context.repo.owner,
118+ repo: context.repo.repo,
119+ comment_id: existingComment.id,
120+ body
121+ });
122+ } else {
123+ await github.rest.issues.createComment({
124+ owner: context.repo.owner,
125+ repo: context.repo.repo,
126+ issue_number: pr.number,
127+ body
128+ });
129+ }
89130
90131 - name : Comment preview failure on PR
91132 if : always() && steps.vercel-preview.outcome != 'success'
92133 uses : actions/github-script@v8
134+ env :
135+ VERCEL_TOKEN : ${{ secrets.VERCEL_TOKEN }}
136+ VERCEL_PROJECT_ID : ${{ secrets.VERCEL_PROJECT_ID }}
137+ VERCEL_ORG_ID : ${{ secrets.VERCEL_ORG_ID }}
93138 with :
94139 script : |
95140 const previewUrl = '${{ steps.vercel-preview.outputs.preview-url }}';
96- const pr = context.payload.workflow_run.pull_requests && context.payload.workflow_run.pull_requests[0];
141+ const workflowRun = context.payload.workflow_run;
142+ const pr = workflowRun?.pull_requests?.[0];
97143 if (!pr) {
98144 core.warning('No pull request metadata available; skipping preview failure comment.');
99145 return;
100146 }
101- const linkLine = previewUrl ? `\n\n🔗 ${previewUrl}` : '\n\n🔗 View the failed deployment in Vercel.';
147+
148+ const fallbackRunUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
149+ const fetchDeploymentUrl = async () => {
150+ if (!process.env.VERCEL_TOKEN || !process.env.VERCEL_PROJECT_ID) {
151+ core.info('Missing Vercel credentials; cannot query deployment API.');
152+ return null;
153+ }
154+ const query = new URLSearchParams({
155+ projectId: process.env.VERCEL_PROJECT_ID,
156+ 'meta-githubCommitSha': workflowRun?.head_sha ?? '',
157+ limit: '1'
158+ });
159+ if (process.env.VERCEL_ORG_ID) {
160+ query.set('teamId', process.env.VERCEL_ORG_ID);
161+ }
162+ if (typeof fetch !== 'function') {
163+ core.info('Fetch API unavailable in this runtime.');
164+ return null;
165+ }
166+ const response = await fetch(`https://api.vercel.com/v6/deployments?${query.toString()}`, {
167+ headers: {
168+ Authorization: `Bearer ${process.env.VERCEL_TOKEN}`
169+ }
170+ });
171+ if (!response.ok) {
172+ core.warning(`Unable to fetch deployment info (status ${response.status}).`);
173+ return null;
174+ }
175+ const data = await response.json();
176+ const deployment = data?.deployments?.[0];
177+ if (deployment?.url) {
178+ return `https://${deployment.url}`;
179+ }
180+ if (deployment?.inspectorUrl) {
181+ return deployment.inspectorUrl.startsWith('http')
182+ ? deployment.inspectorUrl
183+ : `https://${deployment.inspectorUrl}`;
184+ }
185+ return null;
186+ };
187+
188+ let failedDeploymentUrl = previewUrl || await fetchDeploymentUrl();
189+ if (!failedDeploymentUrl) {
190+ failedDeploymentUrl = fallbackRunUrl;
191+ }
192+
193+ const linkText = failedDeploymentUrl === fallbackRunUrl
194+ ? 'View the workflow logs'
195+ : 'Open the failed Vercel deployment';
196+ const linkLine = `\n\n🔗 [${linkText}](${failedDeploymentUrl})`;
102197 await github.rest.issues.createComment({
103198 issue_number: pr.number,
104199 owner: context.repo.owner,
@@ -147,7 +242,7 @@ jobs:
147242 const targetUrl = '${{ steps.vercel-production.outputs.preview-url }}';
148243 const body = targetUrl
149244 ? `❌ Production deployment failed.\n\n🔗 ${targetUrl}\n\nPlease review the Vercel logs.`
150- : '❌ Production deployment failed. Please review the Vercel logs.'
245+ : '❌ Production deployment failed. Please review the Vercel logs.';
151246 await github.rest.repos.createCommitComment({
152247 owner: context.repo.owner,
153248 repo: context.repo.repo,
0 commit comments