Stijnus
commited on
Commit
Β·
b509673
1
Parent(s):
f091409
Update fix
Browse filesmore enhanced UI and more details what is fixed, ect
app/components/@settings/tabs/update/UpdateTab.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { logStore } from '~/lib/stores/logs';
|
|
| 5 |
import { toast } from 'react-toastify';
|
| 6 |
import { Dialog, DialogRoot, DialogTitle, DialogDescription, DialogButton } from '~/components/ui/Dialog';
|
| 7 |
import { classNames } from '~/utils/classNames';
|
|
|
|
| 8 |
|
| 9 |
interface UpdateProgress {
|
| 10 |
stage: 'fetch' | 'pull' | 'install' | 'build' | 'complete';
|
|
@@ -20,6 +21,8 @@ interface UpdateProgress {
|
|
| 20 |
currentCommit?: string;
|
| 21 |
remoteCommit?: string;
|
| 22 |
updateReady?: boolean;
|
|
|
|
|
|
|
| 23 |
};
|
| 24 |
}
|
| 25 |
|
|
@@ -50,15 +53,52 @@ const UpdateProgressDisplay = ({ progress }: { progress: UpdateProgress }) => (
|
|
| 50 |
{progress.details && (
|
| 51 |
<div className="mt-2 text-sm text-gray-600">
|
| 52 |
{progress.details.changedFiles && progress.details.changedFiles.length > 0 && (
|
| 53 |
-
<div className="mt-
|
| 54 |
-
<div className="font-medium">Changed Files:</div>
|
| 55 |
-
<
|
| 56 |
-
{
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
</div>
|
| 63 |
)}
|
| 64 |
{progress.details.totalSize && <div className="mt-1">Total size: {progress.details.totalSize}</div>}
|
|
@@ -410,19 +450,100 @@ const UpdateTab = () => {
|
|
| 410 |
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
| 411 |
|
| 412 |
{/* Show update source information */}
|
| 413 |
-
|
| 414 |
-
<
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
</motion.div>
|
| 427 |
|
| 428 |
{/* Update dialog */}
|
|
@@ -435,40 +556,58 @@ const UpdateTab = () => {
|
|
| 435 |
A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
| 436 |
{isLatestBranch ? 'main' : 'stable'} branch)
|
| 437 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
| 439 |
-
<div className="mb-
|
| 440 |
<p className="font-medium mb-2">Commit Messages:</p>
|
| 441 |
-
<
|
| 442 |
{updateProgress.details.commitMessages.map((msg, index) => (
|
| 443 |
-
<
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
</ul>
|
| 448 |
-
</div>
|
| 449 |
-
)}
|
| 450 |
-
{updateProgress?.details?.changedFiles && (
|
| 451 |
-
<div>
|
| 452 |
-
<p className="font-medium mb-2">Changed Files:</p>
|
| 453 |
-
<ul className="list-disc list-inside space-y-1">
|
| 454 |
-
{updateProgress.details.changedFiles.map((file, index) => (
|
| 455 |
-
<li key={index} className="text-sm text-bolt-elements-textSecondary">
|
| 456 |
-
{file}
|
| 457 |
-
</li>
|
| 458 |
))}
|
| 459 |
-
</
|
| 460 |
</div>
|
| 461 |
)}
|
|
|
|
| 462 |
{updateProgress?.details?.totalSize && (
|
| 463 |
-
<
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
)}
|
| 473 |
</div>
|
| 474 |
</DialogDescription>
|
|
|
|
| 5 |
import { toast } from 'react-toastify';
|
| 6 |
import { Dialog, DialogRoot, DialogTitle, DialogDescription, DialogButton } from '~/components/ui/Dialog';
|
| 7 |
import { classNames } from '~/utils/classNames';
|
| 8 |
+
import { Markdown } from '~/components/chat/Markdown';
|
| 9 |
|
| 10 |
interface UpdateProgress {
|
| 11 |
stage: 'fetch' | 'pull' | 'install' | 'build' | 'complete';
|
|
|
|
| 21 |
currentCommit?: string;
|
| 22 |
remoteCommit?: string;
|
| 23 |
updateReady?: boolean;
|
| 24 |
+
changelog?: string;
|
| 25 |
+
compareUrl?: string;
|
| 26 |
};
|
| 27 |
}
|
| 28 |
|
|
|
|
| 53 |
{progress.details && (
|
| 54 |
<div className="mt-2 text-sm text-gray-600">
|
| 55 |
{progress.details.changedFiles && progress.details.changedFiles.length > 0 && (
|
| 56 |
+
<div className="mt-4">
|
| 57 |
+
<div className="font-medium mb-2">Changed Files:</div>
|
| 58 |
+
<div className="space-y-2">
|
| 59 |
+
{/* Group files by type */}
|
| 60 |
+
{['Modified', 'Added', 'Deleted'].map((type) => {
|
| 61 |
+
const filesOfType = progress.details?.changedFiles?.filter((file) => file.startsWith(type)) || [];
|
| 62 |
+
|
| 63 |
+
if (filesOfType.length === 0) {
|
| 64 |
+
return null;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
return (
|
| 68 |
+
<div key={type} className="space-y-1">
|
| 69 |
+
<div
|
| 70 |
+
className={classNames('text-sm font-medium', {
|
| 71 |
+
'text-blue-500': type === 'Modified',
|
| 72 |
+
'text-green-500': type === 'Added',
|
| 73 |
+
'text-red-500': type === 'Deleted',
|
| 74 |
+
})}
|
| 75 |
+
>
|
| 76 |
+
{type} ({filesOfType.length})
|
| 77 |
+
</div>
|
| 78 |
+
<div className="pl-4 space-y-1">
|
| 79 |
+
{filesOfType.map((file, index) => {
|
| 80 |
+
const fileName = file.split(': ')[1];
|
| 81 |
+
return (
|
| 82 |
+
<div key={index} className="text-sm text-bolt-elements-textSecondary flex items-center gap-2">
|
| 83 |
+
<div
|
| 84 |
+
className={classNames('w-4 h-4', {
|
| 85 |
+
'i-ph:pencil-simple': type === 'Modified',
|
| 86 |
+
'i-ph:plus': type === 'Added',
|
| 87 |
+
'i-ph:trash': type === 'Deleted',
|
| 88 |
+
'text-blue-500': type === 'Modified',
|
| 89 |
+
'text-green-500': type === 'Added',
|
| 90 |
+
'text-red-500': type === 'Deleted',
|
| 91 |
+
})}
|
| 92 |
+
/>
|
| 93 |
+
<span className="font-mono text-xs">{fileName}</span>
|
| 94 |
+
</div>
|
| 95 |
+
);
|
| 96 |
+
})}
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
);
|
| 100 |
+
})}
|
| 101 |
+
</div>
|
| 102 |
</div>
|
| 103 |
)}
|
| 104 |
{progress.details.totalSize && <div className="mt-1">Total size: {progress.details.totalSize}</div>}
|
|
|
|
| 450 |
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
| 451 |
|
| 452 |
{/* Show update source information */}
|
| 453 |
+
{updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && (
|
| 454 |
+
<div className="mt-4 text-sm text-bolt-elements-textSecondary">
|
| 455 |
+
<div className="flex items-center justify-between">
|
| 456 |
+
<div>
|
| 457 |
+
<p>
|
| 458 |
+
Updates are fetched from: <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
| 459 |
+
{isLatestBranch ? 'main' : 'stable'} branch)
|
| 460 |
+
</p>
|
| 461 |
+
<p className="mt-1">
|
| 462 |
+
Current version: <span className="font-mono">{updateProgress.details.currentCommit}</span>
|
| 463 |
+
<span className="mx-2">β</span>
|
| 464 |
+
Latest version: <span className="font-mono">{updateProgress.details.remoteCommit}</span>
|
| 465 |
+
</p>
|
| 466 |
+
</div>
|
| 467 |
+
{updateProgress?.details?.compareUrl && (
|
| 468 |
+
<a
|
| 469 |
+
href={updateProgress.details.compareUrl}
|
| 470 |
+
target="_blank"
|
| 471 |
+
rel="noopener noreferrer"
|
| 472 |
+
className={classNames(
|
| 473 |
+
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
| 474 |
+
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
| 475 |
+
'hover:bg-purple-500/10 hover:text-purple-500',
|
| 476 |
+
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
| 477 |
+
'text-bolt-elements-textPrimary',
|
| 478 |
+
'transition-colors duration-200',
|
| 479 |
+
'w-fit',
|
| 480 |
+
)}
|
| 481 |
+
>
|
| 482 |
+
<div className="i-ph:github-logo w-4 h-4" />
|
| 483 |
+
View Changes on GitHub
|
| 484 |
+
</a>
|
| 485 |
+
)}
|
| 486 |
+
</div>
|
| 487 |
+
{updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && (
|
| 488 |
+
<div className="mt-2 flex items-center gap-2">
|
| 489 |
+
<div className="i-ph:git-diff text-purple-500 w-4 h-4" />
|
| 490 |
+
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
| 491 |
+
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
| 492 |
+
</div>
|
| 493 |
+
)}
|
| 494 |
+
</div>
|
| 495 |
+
)}
|
| 496 |
+
|
| 497 |
+
{/* Add this before the changed files section */}
|
| 498 |
+
{updateProgress?.details?.changelog && (
|
| 499 |
+
<div className="mb-6">
|
| 500 |
+
<div className="flex items-center gap-2 mb-2">
|
| 501 |
+
<div className="i-ph:scroll text-purple-500 w-5 h-5" />
|
| 502 |
+
<p className="font-medium">Changelog</p>
|
| 503 |
+
</div>
|
| 504 |
+
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-4 overflow-auto max-h-[300px]">
|
| 505 |
+
<div className="prose dark:prose-invert prose-sm max-w-none">
|
| 506 |
+
<Markdown>{updateProgress.details.changelog}</Markdown>
|
| 507 |
+
</div>
|
| 508 |
+
</div>
|
| 509 |
+
</div>
|
| 510 |
+
)}
|
| 511 |
+
|
| 512 |
+
{/* Add this in the update status card, after the commit info */}
|
| 513 |
+
{updateProgress?.details?.compareUrl && (
|
| 514 |
+
<div className="mt-4">
|
| 515 |
+
<a
|
| 516 |
+
href={updateProgress.details.compareUrl}
|
| 517 |
+
target="_blank"
|
| 518 |
+
rel="noopener noreferrer"
|
| 519 |
+
className={classNames(
|
| 520 |
+
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
| 521 |
+
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
| 522 |
+
'hover:bg-purple-500/10 hover:text-purple-500',
|
| 523 |
+
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
| 524 |
+
'text-bolt-elements-textPrimary',
|
| 525 |
+
'transition-colors duration-200',
|
| 526 |
+
'w-fit',
|
| 527 |
+
)}
|
| 528 |
+
>
|
| 529 |
+
<div className="i-ph:github-logo w-4 h-4" />
|
| 530 |
+
View Changes on GitHub
|
| 531 |
+
</a>
|
| 532 |
+
</div>
|
| 533 |
+
)}
|
| 534 |
+
|
| 535 |
+
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
| 536 |
+
<div className="mb-6">
|
| 537 |
+
<p className="font-medium mb-2">Changes in this Update:</p>
|
| 538 |
+
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-4 overflow-auto max-h-[400px]">
|
| 539 |
+
<div className="prose dark:prose-invert prose-sm max-w-none">
|
| 540 |
+
{updateProgress.details.commitMessages.map((section, index) => (
|
| 541 |
+
<Markdown key={index}>{section}</Markdown>
|
| 542 |
+
))}
|
| 543 |
+
</div>
|
| 544 |
+
</div>
|
| 545 |
+
</div>
|
| 546 |
+
)}
|
| 547 |
</motion.div>
|
| 548 |
|
| 549 |
{/* Update dialog */}
|
|
|
|
| 556 |
A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
| 557 |
{isLatestBranch ? 'main' : 'stable'} branch)
|
| 558 |
</p>
|
| 559 |
+
|
| 560 |
+
{updateProgress?.details?.compareUrl && (
|
| 561 |
+
<div className="mb-6">
|
| 562 |
+
<a
|
| 563 |
+
href={updateProgress.details.compareUrl}
|
| 564 |
+
target="_blank"
|
| 565 |
+
rel="noopener noreferrer"
|
| 566 |
+
className={classNames(
|
| 567 |
+
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
| 568 |
+
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
| 569 |
+
'hover:bg-purple-500/10 hover:text-purple-500',
|
| 570 |
+
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
| 571 |
+
'text-bolt-elements-textPrimary',
|
| 572 |
+
'transition-colors duration-200',
|
| 573 |
+
'w-fit',
|
| 574 |
+
)}
|
| 575 |
+
>
|
| 576 |
+
<div className="i-ph:github-logo w-4 h-4" />
|
| 577 |
+
View Changes on GitHub
|
| 578 |
+
</a>
|
| 579 |
+
</div>
|
| 580 |
+
)}
|
| 581 |
+
|
| 582 |
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
| 583 |
+
<div className="mb-6">
|
| 584 |
<p className="font-medium mb-2">Commit Messages:</p>
|
| 585 |
+
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-3 space-y-2">
|
| 586 |
{updateProgress.details.commitMessages.map((msg, index) => (
|
| 587 |
+
<div key={index} className="text-sm text-bolt-elements-textSecondary flex items-start gap-2">
|
| 588 |
+
<div className="i-ph:git-commit text-purple-500 w-4 h-4 mt-0.5 flex-shrink-0" />
|
| 589 |
+
<span>{msg}</span>
|
| 590 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
))}
|
| 592 |
+
</div>
|
| 593 |
</div>
|
| 594 |
)}
|
| 595 |
+
|
| 596 |
{updateProgress?.details?.totalSize && (
|
| 597 |
+
<div className="flex items-center gap-4 text-sm text-bolt-elements-textSecondary">
|
| 598 |
+
<div className="flex items-center gap-2">
|
| 599 |
+
<div className="i-ph:file text-purple-500 w-4 h-4" />
|
| 600 |
+
Total size: {updateProgress.details.totalSize}
|
| 601 |
+
</div>
|
| 602 |
+
{updateProgress?.details?.additions !== undefined &&
|
| 603 |
+
updateProgress?.details?.deletions !== undefined && (
|
| 604 |
+
<div className="flex items-center gap-2">
|
| 605 |
+
<div className="i-ph:git-diff text-purple-500 w-4 h-4" />
|
| 606 |
+
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
| 607 |
+
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
| 608 |
+
</div>
|
| 609 |
+
)}
|
| 610 |
+
</div>
|
| 611 |
)}
|
| 612 |
</div>
|
| 613 |
</DialogDescription>
|
app/routes/api.update.ts
CHANGED
|
@@ -24,6 +24,8 @@ interface UpdateProgress {
|
|
| 24 |
currentCommit?: string;
|
| 25 |
remoteCommit?: string;
|
| 26 |
updateReady?: boolean;
|
|
|
|
|
|
|
| 27 |
};
|
| 28 |
}
|
| 29 |
|
|
@@ -231,9 +233,103 @@ export const action: ActionFunction = async ({ request }) => {
|
|
| 231 |
// Get commit messages between current and remote
|
| 232 |
try {
|
| 233 |
const { stdout: logOutput } = await execAsync(
|
| 234 |
-
`git log --
|
| 235 |
);
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
} catch {
|
| 238 |
// Handle silently - empty commitMessages array will be used
|
| 239 |
}
|
|
@@ -260,6 +356,15 @@ export const action: ActionFunction = async ({ request }) => {
|
|
| 260 |
return;
|
| 261 |
}
|
| 262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
// We have changes, send the details
|
| 264 |
sendProgress({
|
| 265 |
stage: 'fetch',
|
|
@@ -274,6 +379,8 @@ export const action: ActionFunction = async ({ request }) => {
|
|
| 274 |
currentCommit: currentCommit.trim().substring(0, 7),
|
| 275 |
remoteCommit: remoteCommit.trim().substring(0, 7),
|
| 276 |
updateReady: true,
|
|
|
|
|
|
|
| 277 |
},
|
| 278 |
});
|
| 279 |
|
|
@@ -292,6 +399,8 @@ export const action: ActionFunction = async ({ request }) => {
|
|
| 292 |
currentCommit: currentCommit.trim().substring(0, 7),
|
| 293 |
remoteCommit: remoteCommit.trim().substring(0, 7),
|
| 294 |
updateReady: true,
|
|
|
|
|
|
|
| 295 |
},
|
| 296 |
});
|
| 297 |
return;
|
|
@@ -378,3 +487,87 @@ export const action: ActionFunction = async ({ request }) => {
|
|
| 378 |
);
|
| 379 |
}
|
| 380 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
currentCommit?: string;
|
| 25 |
remoteCommit?: string;
|
| 26 |
updateReady?: boolean;
|
| 27 |
+
changelog?: string;
|
| 28 |
+
compareUrl?: string;
|
| 29 |
};
|
| 30 |
}
|
| 31 |
|
|
|
|
| 233 |
// Get commit messages between current and remote
|
| 234 |
try {
|
| 235 |
const { stdout: logOutput } = await execAsync(
|
| 236 |
+
`git log --pretty=format:"%h|%s|%aI" ${currentCommit.trim()}..${remoteCommit.trim()}`,
|
| 237 |
);
|
| 238 |
+
|
| 239 |
+
// Parse and group commits by type
|
| 240 |
+
const commits = logOutput
|
| 241 |
+
.split('\n')
|
| 242 |
+
.filter(Boolean)
|
| 243 |
+
.map((line) => {
|
| 244 |
+
const [hash, subject, timestamp] = line.split('|');
|
| 245 |
+
let type = 'other';
|
| 246 |
+
let message = subject;
|
| 247 |
+
|
| 248 |
+
if (subject.startsWith('feat:') || subject.startsWith('feature:')) {
|
| 249 |
+
type = 'feature';
|
| 250 |
+
message = subject.replace(/^feat(?:ure)?:/, '').trim();
|
| 251 |
+
} else if (subject.startsWith('fix:')) {
|
| 252 |
+
type = 'fix';
|
| 253 |
+
message = subject.replace(/^fix:/, '').trim();
|
| 254 |
+
} else if (subject.startsWith('docs:')) {
|
| 255 |
+
type = 'docs';
|
| 256 |
+
message = subject.replace(/^docs:/, '').trim();
|
| 257 |
+
} else if (subject.startsWith('style:')) {
|
| 258 |
+
type = 'style';
|
| 259 |
+
message = subject.replace(/^style:/, '').trim();
|
| 260 |
+
} else if (subject.startsWith('refactor:')) {
|
| 261 |
+
type = 'refactor';
|
| 262 |
+
message = subject.replace(/^refactor:/, '').trim();
|
| 263 |
+
} else if (subject.startsWith('perf:')) {
|
| 264 |
+
type = 'perf';
|
| 265 |
+
message = subject.replace(/^perf:/, '').trim();
|
| 266 |
+
} else if (subject.startsWith('test:')) {
|
| 267 |
+
type = 'test';
|
| 268 |
+
message = subject.replace(/^test:/, '').trim();
|
| 269 |
+
} else if (subject.startsWith('build:')) {
|
| 270 |
+
type = 'build';
|
| 271 |
+
message = subject.replace(/^build:/, '').trim();
|
| 272 |
+
} else if (subject.startsWith('ci:')) {
|
| 273 |
+
type = 'ci';
|
| 274 |
+
message = subject.replace(/^ci:/, '').trim();
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
return {
|
| 278 |
+
hash,
|
| 279 |
+
type,
|
| 280 |
+
message,
|
| 281 |
+
timestamp: new Date(timestamp),
|
| 282 |
+
};
|
| 283 |
+
});
|
| 284 |
+
|
| 285 |
+
// Group commits by type
|
| 286 |
+
const groupedCommits = commits.reduce(
|
| 287 |
+
(acc, commit) => {
|
| 288 |
+
if (!acc[commit.type]) {
|
| 289 |
+
acc[commit.type] = [];
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
acc[commit.type].push(commit);
|
| 293 |
+
|
| 294 |
+
return acc;
|
| 295 |
+
},
|
| 296 |
+
{} as Record<string, typeof commits>,
|
| 297 |
+
);
|
| 298 |
+
|
| 299 |
+
// Format commit messages with emojis and timestamps
|
| 300 |
+
const formattedMessages = Object.entries(groupedCommits).map(([type, commits]) => {
|
| 301 |
+
const emoji = {
|
| 302 |
+
feature: 'β¨',
|
| 303 |
+
fix: 'π',
|
| 304 |
+
docs: 'π',
|
| 305 |
+
style: 'π',
|
| 306 |
+
refactor: 'β»οΈ',
|
| 307 |
+
perf: 'β‘',
|
| 308 |
+
test: 'π§ͺ',
|
| 309 |
+
build: 'π οΈ',
|
| 310 |
+
ci: 'βοΈ',
|
| 311 |
+
other: 'π',
|
| 312 |
+
}[type];
|
| 313 |
+
|
| 314 |
+
const title = {
|
| 315 |
+
feature: 'Features',
|
| 316 |
+
fix: 'Bug Fixes',
|
| 317 |
+
docs: 'Documentation',
|
| 318 |
+
style: 'Styles',
|
| 319 |
+
refactor: 'Code Refactoring',
|
| 320 |
+
perf: 'Performance',
|
| 321 |
+
test: 'Tests',
|
| 322 |
+
build: 'Build',
|
| 323 |
+
ci: 'CI',
|
| 324 |
+
other: 'Other Changes',
|
| 325 |
+
}[type];
|
| 326 |
+
|
| 327 |
+
return `### ${emoji} ${title}\n\n${commits
|
| 328 |
+
.map((c) => `* ${c.message} (${c.hash.substring(0, 7)}) - ${c.timestamp.toLocaleString()}`)
|
| 329 |
+
.join('\n')}`;
|
| 330 |
+
});
|
| 331 |
+
|
| 332 |
+
commitMessages = formattedMessages;
|
| 333 |
} catch {
|
| 334 |
// Handle silently - empty commitMessages array will be used
|
| 335 |
}
|
|
|
|
| 356 |
return;
|
| 357 |
}
|
| 358 |
|
| 359 |
+
// Fetch changelog
|
| 360 |
+
sendProgress({
|
| 361 |
+
stage: 'fetch',
|
| 362 |
+
message: 'Fetching changelog...',
|
| 363 |
+
progress: 95,
|
| 364 |
+
});
|
| 365 |
+
|
| 366 |
+
const changelog = await fetchChangelog(currentCommit.trim(), remoteCommit.trim());
|
| 367 |
+
|
| 368 |
// We have changes, send the details
|
| 369 |
sendProgress({
|
| 370 |
stage: 'fetch',
|
|
|
|
| 379 |
currentCommit: currentCommit.trim().substring(0, 7),
|
| 380 |
remoteCommit: remoteCommit.trim().substring(0, 7),
|
| 381 |
updateReady: true,
|
| 382 |
+
changelog,
|
| 383 |
+
compareUrl: `https://github.com/stackblitz-labs/bolt.diy/compare/${currentCommit.trim().substring(0, 7)}...${remoteCommit.trim().substring(0, 7)}`,
|
| 384 |
},
|
| 385 |
});
|
| 386 |
|
|
|
|
| 399 |
currentCommit: currentCommit.trim().substring(0, 7),
|
| 400 |
remoteCommit: remoteCommit.trim().substring(0, 7),
|
| 401 |
updateReady: true,
|
| 402 |
+
changelog,
|
| 403 |
+
compareUrl: `https://github.com/stackblitz-labs/bolt.diy/compare/${currentCommit.trim().substring(0, 7)}...${remoteCommit.trim().substring(0, 7)}`,
|
| 404 |
},
|
| 405 |
});
|
| 406 |
return;
|
|
|
|
| 487 |
);
|
| 488 |
}
|
| 489 |
};
|
| 490 |
+
|
| 491 |
+
// Add this function to fetch the changelog
|
| 492 |
+
async function fetchChangelog(currentCommit: string, remoteCommit: string): Promise<string> {
|
| 493 |
+
try {
|
| 494 |
+
// First try to get the changelog.md content
|
| 495 |
+
const { stdout: changelogContent } = await execAsync('git show upstream/main:changelog.md');
|
| 496 |
+
|
| 497 |
+
// If we have a changelog, return it
|
| 498 |
+
if (changelogContent) {
|
| 499 |
+
return changelogContent;
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
// If no changelog.md, generate one in a similar format
|
| 503 |
+
let changelog = '# Changes in this Update\n\n';
|
| 504 |
+
|
| 505 |
+
// Get commit messages grouped by type
|
| 506 |
+
const { stdout: commitLog } = await execAsync(
|
| 507 |
+
`git log --pretty=format:"%h|%s|%b" ${currentCommit.trim()}..${remoteCommit.trim()}`,
|
| 508 |
+
);
|
| 509 |
+
|
| 510 |
+
const commits = commitLog.split('\n').filter(Boolean);
|
| 511 |
+
const categorizedCommits: Record<string, string[]> = {
|
| 512 |
+
'β¨ Features': [],
|
| 513 |
+
'π Bug Fixes': [],
|
| 514 |
+
'π Documentation': [],
|
| 515 |
+
'π Styles': [],
|
| 516 |
+
'β»οΈ Code Refactoring': [],
|
| 517 |
+
'β‘ Performance': [],
|
| 518 |
+
'π§ͺ Tests': [],
|
| 519 |
+
'π οΈ Build': [],
|
| 520 |
+
'βοΈ CI': [],
|
| 521 |
+
'π Other Changes': [],
|
| 522 |
+
};
|
| 523 |
+
|
| 524 |
+
// Categorize commits
|
| 525 |
+
for (const commit of commits) {
|
| 526 |
+
const [hash, subject] = commit.split('|');
|
| 527 |
+
let category = 'π Other Changes';
|
| 528 |
+
|
| 529 |
+
if (subject.startsWith('feat:') || subject.startsWith('feature:')) {
|
| 530 |
+
category = 'β¨ Features';
|
| 531 |
+
} else if (subject.startsWith('fix:')) {
|
| 532 |
+
category = 'π Bug Fixes';
|
| 533 |
+
} else if (subject.startsWith('docs:')) {
|
| 534 |
+
category = 'π Documentation';
|
| 535 |
+
} else if (subject.startsWith('style:')) {
|
| 536 |
+
category = 'π Styles';
|
| 537 |
+
} else if (subject.startsWith('refactor:')) {
|
| 538 |
+
category = 'β»οΈ Code Refactoring';
|
| 539 |
+
} else if (subject.startsWith('perf:')) {
|
| 540 |
+
category = 'β‘ Performance';
|
| 541 |
+
} else if (subject.startsWith('test:')) {
|
| 542 |
+
category = 'π§ͺ Tests';
|
| 543 |
+
} else if (subject.startsWith('build:')) {
|
| 544 |
+
category = 'π οΈ Build';
|
| 545 |
+
} else if (subject.startsWith('ci:')) {
|
| 546 |
+
category = 'βοΈ CI';
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
const message = subject.includes(':') ? subject.split(':')[1].trim() : subject.trim();
|
| 550 |
+
categorizedCommits[category].push(`* ${message} (${hash.substring(0, 7)})`);
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
// Build changelog content
|
| 554 |
+
for (const [category, commits] of Object.entries(categorizedCommits)) {
|
| 555 |
+
if (commits.length > 0) {
|
| 556 |
+
changelog += `\n## ${category}\n\n${commits.join('\n')}\n`;
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
// Add stats
|
| 561 |
+
const { stdout: stats } = await execAsync(`git diff --shortstat ${currentCommit.trim()}..${remoteCommit.trim()}`);
|
| 562 |
+
|
| 563 |
+
if (stats) {
|
| 564 |
+
changelog += '\n## π Stats\n\n';
|
| 565 |
+
changelog += `${stats.trim()}\n`;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
return changelog;
|
| 569 |
+
} catch (error) {
|
| 570 |
+
console.error('Error fetching changelog:', error);
|
| 571 |
+
return 'Unable to fetch changelog';
|
| 572 |
+
}
|
| 573 |
+
}
|